diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.builder/lib/spock-core-1.2-groovy-2.4.jar b/base-test/org.eclipse.jdt.groovy.core.tests.builder/lib/spock-core-1.3-groovy-2.4.jar similarity index 77% rename from base-test/org.eclipse.jdt.groovy.core.tests.builder/lib/spock-core-1.2-groovy-2.4.jar rename to base-test/org.eclipse.jdt.groovy.core.tests.builder/lib/spock-core-1.3-groovy-2.4.jar index c84e919c3c..2a220d01ca 100644 Binary files a/base-test/org.eclipse.jdt.groovy.core.tests.builder/lib/spock-core-1.2-groovy-2.4.jar and b/base-test/org.eclipse.jdt.groovy.core.tests.builder/lib/spock-core-1.3-groovy-2.4.jar differ diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/builder/BasicGroovyBuildTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/builder/BasicGroovyBuildTests.java index ad9aeadfa5..05eccc0309 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/builder/BasicGroovyBuildTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/builder/BasicGroovyBuildTests.java @@ -1998,21 +1998,18 @@ public void testSpock_GRE558() throws Exception { assumeFalse(isAtLeastGroovy(30)); // TODO: Remove when spock-core supports Groovy 3 IPath[] paths = createSimpleProject("Project", true); - env.addJar(paths[0], "lib/spock-core-1.2-groovy-2.4.jar"); + env.addJar(paths[0], "lib/spock-core-1.3-groovy-2.4.jar"); env.addEntry(paths[0], JavaCore.newContainerEntry(new Path("org.eclipse.jdt.junit.JUNIT_CONTAINER/4"))); env.addGroovyClass(paths[1], "", "MyTest", //@formatter:off - "import org.junit.runner.RunWith\n" + - "import spock.lang.Specification\n" + - "\n" + - "final class MyTest extends Specification {\n" + - " def aField\n" + - " def aMethod() {\n" + - " expect:\n" + - " println 'hello'\n" + + "final class MyTest extends spock.lang.Specification {\n" + + " def prop\n" + + " def meth() {\n" + + " expect:\n" + + " 'hello' != 'world'\n" + " }\n" + - " static void main(String[] argv) {\n" + + " static main(args) {\n" + " print 'success'\n" + " }\n" + "}\n"); @@ -2034,35 +2031,27 @@ public void testSpock_GRE605_1() throws Exception { assumeFalse(isAtLeastGroovy(30)); // TODO: Remove when spock-core supports Groovy 3 IPath[] paths = createSimpleProject("Project", true); - env.addJar(paths[0], "lib/spock-core-1.2-groovy-2.4.jar"); + env.addJar(paths[0], "lib/spock-core-1.3-groovy-2.4.jar"); env.addEntry(paths[0], JavaCore.newContainerEntry(new Path("org.eclipse.jdt.junit.JUNIT_CONTAINER/4"))); env.addGroovyClass(paths[1], "", "FoobarSpec", //@formatter:off - "import spock.lang.Specification\n" + - "\n" + - "class FoobarSpec extends Specification {\n" + - " \n" + - " Foobar barbar\n" + - " \n" + - " def example() {\n" + - " when: \n" + - " def foobar = new Foobar()\n" + - " \n" + - " then:\n" + - " foobar.baz == 42\n" + - " }\n" + + "class FoobarSpec extends spock.lang.Specification {\n" + + " private Foobar field\n" + + " def example() {\n" + + " when: \n" + + " def foobar = new Foobar()\n" + " \n" + + " then:\n" + + " foobar.baz == 42\n" + + " }\n" + "}"); //@formatter:on env.addGroovyClass(paths[1], "", "Foobar", //@formatter:off "class Foobar {\n" + - "\n" + - "def baz = 42\n" + - "//def quux = 36\n" + - "\n" + + " def baz = 42\n" + "}\n"); //@formatter:on @@ -2077,10 +2066,8 @@ public void testSpock_GRE605_1() throws Exception { env.addGroovyClass(paths[1], "", "Foobar", //@formatter:off "class Foobar {\n" + - "\n" + - "def baz = 42\n" + - "def quux = 36\n" + - "\n" + + " def baz = 42\n" + + " def xyz = 36\n" + "}\n"); //@formatter:on @@ -2106,34 +2093,27 @@ public void testSpock_GRE605_2() throws Exception { assumeFalse(isAtLeastGroovy(30)); // TODO: Remove when spock-core supports Groovy 3 IPath[] paths = createSimpleProject("Project", true); - env.addJar(paths[0], "lib/spock-core-1.2-groovy-2.4.jar"); + env.addJar(paths[0], "lib/spock-core-1.3-groovy-2.4.jar"); env.addEntry(paths[0], JavaCore.newContainerEntry(new Path("org.eclipse.jdt.junit.JUNIT_CONTAINER/4"))); env.addGroovyClass(paths[1], "", "FoobarSpec", //@formatter:off - "import spock.lang.Specification\n" + - "\n" + - "class FoobarSpec extends Specification {\n" + - " \n" + - " Foobar foob\n" + - " def example() {\n" + - " when: \n" + - " def foobar = new Foobar()\n" + - " \n" + - " then:\n" + - " foobar.baz == 42\n" + - " }\n" + + "class FoobarSpec extends spock.lang.Specification {\n" + + " private Foobar field\n" + + " def example() {\n" + + " given: \n" + + " def foobar = new Foobar()\n" + " \n" + + " expect:\n" + + " foobar.baz == 42\n" + + " }\n" + "}"); //@formatter:on env.addGroovyClass(paths[1], "", "Foobar", //@formatter:off "class Foobar {\n" + - "\n" + - "def baz = 42\n" + - "//def quux = 36\n" + - "\n" + + " def baz = 42\n" + "}\n"); //@formatter:on @@ -2144,10 +2124,8 @@ public void testSpock_GRE605_2() throws Exception { env.addGroovyClass(paths[1], "", "Foobar", //@formatter:off "class Foobar {\n" + - "\n" + - "def baz = 42\n" + - "def quux = 36\n" + - "\n" + + " def baz = 42\n" + + " def xyz = 36\n" + "}\n"); //@formatter:on diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/SpockInferencingTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/SpockInferencingTests.java index 8b0eb33dd8..7c25f37c7d 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/SpockInferencingTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/SpockInferencingTests.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -21,32 +21,18 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.JavaCore; -import org.junit.After; import org.junit.Before; import org.junit.Test; public final class SpockInferencingTests extends InferencingTestSuite { - private String xforms; - @Before public void setUp() throws Exception { assumeFalse(isAtLeastGroovy(30)); // TODO: Remove when spock-core supports Groovy 3 IPath projectPath = project.getFullPath(); - env.addJar(projectPath, "lib/spock-core-1.2-groovy-2.4.jar"); + env.addJar(projectPath, "lib/spock-core-1.3-groovy-2.4.jar"); env.addEntry(projectPath, JavaCore.newContainerEntry(new Path("org.eclipse.jdt.junit.JUNIT_CONTAINER/4"))); - - xforms = System.setProperty("greclipse.globalTransformsInReconcile", "org.spockframework.compiler.SpockTransform"); - } - - @After - public void tearDown() { - if (xforms == null) { - System.clearProperty("greclipse.globalTransformsInReconcile"); - } else { - System.setProperty("greclipse.globalTransformsInReconcile", xforms); - } } @Test @@ -54,6 +40,7 @@ public void testBasics() throws Exception { createUnit("foo", "Bar", "package foo; class Bar {\n Integer baz\n}"); String source = + //@formatter:off "final class SpockTests extends spock.lang.Specification {\n" + " void 'test the basics'() {\n" + " given:\n" + @@ -66,6 +53,7 @@ public void testBasics() throws Exception { " bar != new foo.Bar(baz:42)\n" + " }\n" + "}\n"; + //@formatter:on int offset = source.indexOf("bar"); assertType(source, offset, offset + 3, "foo.Bar"); @@ -74,9 +62,75 @@ public void testBasics() throws Exception { assertType(source, offset, offset + 3, "foo.Bar"); } + @Test + public void testEqualsCheck() throws Exception { + createUnit("foo", "Bar", "package foo; class Bar {\n Integer baz\n}"); + + String source = + //@formatter:off + "final class SpockTests extends spock.lang.Specification {\n" + + " void 'test the property'() {\n" + + " given:\n" + + " def bar = new foo.Bar()\n" + + " \n" + + " expect:\n" + + " !bar.equals(null)\n" + + " }\n" + + "}\n"; + //@formatter:on + + int offset = source.lastIndexOf("equals"); + assertType(source, offset, offset + 6, "java.lang.Boolean"); + assertDeclaringType(source, offset, offset + 6, "java.lang.Object"); + } + + @Test + public void testGetterCheck() throws Exception { + createUnit("foo", "Bar", "package foo; class Bar {\n Integer baz\n}"); + + String source = + //@formatter:off + "final class SpockTests extends spock.lang.Specification {\n" + + " void 'test the property'() {\n" + + " given:\n" + + " def bar = new foo.Bar(baz: 42)\n" + + " \n" + + " expect:\n" + + " bar.getBaz() == 42\n" + + " }\n" + + "}\n"; + //@formatter:on + + int offset = source.lastIndexOf("getBaz"); + assertType(source, offset, offset + 6, "java.lang.Integer"); + assertDeclaringType(source, offset, offset + 6, "foo.Bar"); + } + + @Test + public void testPropertyCheck() throws Exception { + createUnit("foo", "Bar", "package foo; class Bar {\n Integer baz\n}"); + + String source = + //@formatter:off + "final class SpockTests extends spock.lang.Specification {\n" + + " void 'test the property'() {\n" + + " given:\n" + + " def bar = new foo.Bar(baz: 42)\n" + + " \n" + + " expect:\n" + + " bar.baz == 42\n" + + " }\n" + + "}\n"; + //@formatter:on + + int offset = source.lastIndexOf("baz"); + assertType(source, offset, offset + 3, "java.lang.Integer"); + } + @Test // https://github.com/groovy/groovy-eclipse/issues/812 - public void testDataTable() { + public void testDataTableChecks() { String source = + //@formatter:off "final class SpockTests extends spock.lang.Specification {\n" + " @spock.lang.Unroll\n" + " void 'test #a == #b'() {\n" + @@ -88,6 +142,7 @@ public void testDataTable() { " 2 | a\n" + " }\n" + "}\n"; + //@formatter:on int offset = source.indexOf("a == b"); assertType(source, offset, offset + 1, "java.lang.Object"); diff --git a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/TypeInferencingVisitorWithRequestor.java b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/TypeInferencingVisitorWithRequestor.java index 2edcf76970..e7ba6c40e5 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/TypeInferencingVisitorWithRequestor.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/TypeInferencingVisitorWithRequestor.java @@ -1429,17 +1429,26 @@ public void visitMethodCallExpression(MethodCallExpression node) { visitMethodOverrides(type); } - MethodNode meth; // if return type depends on any Closure argument return types, deal with that now - if (t.declaration instanceof MethodNode && (meth = (MethodNode) t.declaration).getGenericsTypes() != null && - Arrays.stream(meth.getParameters()).anyMatch(p -> p.getType().equals(VariableScope.CLOSURE_CLASS_NODE))) { - scope.setMethodCallArgumentTypes(getMethodCallArgumentTypes(node)); - scope.setMethodCallGenericsTypes(getMethodCallGenericsTypes(node)); - try { - boolean isStatic = (node.getObjectExpression() instanceof ClassExpression); - returnType = lookupExpressionType(node.getMethod(), objExprType, isStatic, scope).type; - } finally { - scope.setMethodCallArgumentTypes(null); - scope.setMethodCallGenericsTypes(null); + if (t.declaration instanceof MethodNode) { + MethodNode meth = (MethodNode) t.declaration; + // if return type depends on any Closure argument return types, deal with that + if (meth.getGenericsTypes() != null && Arrays.stream(meth.getParameters()) + .anyMatch(p -> p.getType().equals(VariableScope.CLOSURE_CLASS_NODE))) { + scope.setMethodCallArgumentTypes(getMethodCallArgumentTypes(node)); + scope.setMethodCallGenericsTypes(getMethodCallGenericsTypes(node)); + try { + boolean isStatic = (node.getObjectExpression() instanceof ClassExpression); + returnType = lookupExpressionType(node.getMethod(), objExprType, isStatic, scope).type; + } finally { + scope.setMethodCallArgumentTypes(null); + scope.setMethodCallGenericsTypes(null); + } + } else if (t.declaringType.getName().equals("org.spockframework.runtime.ValueRecorder")) { + switch (meth.getName()) { + case "record": + case "realizeNas": + returnType = primaryTypeStack.removeLast(); + } } } @@ -2374,7 +2383,44 @@ private boolean isPrimaryExpression(Expression expr) { result[0] = (expr == statement.getExpression()); } }); - return result[0]; + if (result[0]) return true; + } + return isSpockValueRecorderArgument(expr); + } + + /** + * Spock rewrites the "expect" statements from foo.bar == baz to: + *
+     * org.spockframework.runtime.SpockRuntime.verifyCondition(
+     *   $spock_errorCollector,
+     *   $spock_valueRecorder.reset(),
+     *   foo == bar.baz, 14, 5, null,
+     *   $spock_valueRecorder.record(
+     *     $spock_valueRecorder.startRecordingValue(3),
+     *     (
+     *       $spock_valueRecorder.record(
+     *         $spock_valueRecorder.startRecordingValue(1),
+     *         $spock_valueRecorder.record(
+     *           $spock_valueRecorder.startRecordingValue(0), foo).bar
+     *       ) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), baz)
+     *     )
+     *   )
+     * )
+     * 
+ * + * ValueRecorder.record is defined as: public Object record(int index, Object value). + * So, we want to save the inferred type of the last argument so it can be used as the return type. + */ + private boolean isSpockValueRecorderArgument(Expression expr) { + VariableScope.CallAndType cat = scopes.getLast().getEnclosingMethodCallExpression(); + if (cat != null && cat.declaration instanceof MethodNode && cat.declaringType.getName().equals("org.spockframework.runtime.ValueRecorder")) { + ArgumentListExpression args = (ArgumentListExpression) cat.call.getArguments(); + MethodNode meth = (MethodNode) cat.declaration; + switch (meth.getName()) { + case "record": + case "realizeNas": + return expr == DefaultGroovyMethods.last(args.getExpressions()); + } } return false; } diff --git a/ide-test/org.codehaus.groovy.eclipse.tests/src/org/codehaus/groovy/eclipse/test/GroovyEclipseTestSuite.groovy b/ide-test/org.codehaus.groovy.eclipse.tests/src/org/codehaus/groovy/eclipse/test/GroovyEclipseTestSuite.groovy index 77c7a94078..df21cae446 100644 --- a/ide-test/org.codehaus.groovy.eclipse.tests/src/org/codehaus/groovy/eclipse/test/GroovyEclipseTestSuite.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.tests/src/org/codehaus/groovy/eclipse/test/GroovyEclipseTestSuite.groovy @@ -1,11 +1,11 @@ /* - * Copyright 2009-2018 the original author or authors. + * Copyright 2009-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -177,13 +177,13 @@ abstract class GroovyEclipseTestSuite { testProject.addClasspathEntry(JavaCore.newContainerEntry(path, true)) } - protected final void addJUnit(int n) { + protected final void addJUnit(int n) { assert n >= 3 && n <= 5 addClasspathContainer(new Path("org.eclipse.jdt.junit.JUNIT_CONTAINER/$n")) } protected final void addSpock() { def bundle = Platform.getBundle('org.eclipse.jdt.groovy.core.tests.builder') - URL jarUrl = FileLocator.toFileURL(bundle.getEntry('lib/spock-core-1.2-groovy-2.4.jar')) + URL jarUrl = FileLocator.toFileURL(bundle.getEntry('lib/spock-core-1.3-groovy-2.4.jar')) testProject.addExternalLibrary(new Path(jarUrl.file)) }