diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java index b9369a164a..06c9e156dc 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java @@ -788,19 +788,41 @@ public void testStaticProperty3() { public void testStaticProperty4() { //@formatter:off String[] sources = { - "Super.groovy", - "class Super {\n" + - " def static getSql() { 'sql' }\n" + + "Script.groovy", + "abstract class A {\n" + + " static getB() { 'bee' }\n" + "}\n" + - "class Sub extends Super {\n" + - " def static m() {\n" + - " sql.charAt(0)\n" + + "class C extends A {\n" + + " static m() {\n" + + " print b.charAt(0)\n" + + " }\n" + + "}\n" + + "new C().m()\n", + }; + //@formatter:on + + runConformTest(sources, "b"); + } + + @Test + public void testStaticProperty4a() { + //@formatter:off + String[] sources = { + "C.groovy", + "class C extends A {\n" + + " static main(args) {\n" + + " print b.charAt(0)\n" + " }\n" + "}\n", + + "A.groovy", + "abstract class A {\n" + + " static getB() { 'bee' }\n" + + "}\n", }; //@formatter:on - runNegativeTest(sources, ""); + runConformTest(sources, "b"); } @Test // GRECLIPSE-364 diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/InnerClassTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/InnerClassTests.java index c04720d434..665ca42408 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/InnerClassTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/InnerClassTests.java @@ -15,6 +15,7 @@ */ package org.eclipse.jdt.groovy.core.tests.basic; +import org.junit.Ignore; import org.junit.Test; public final class InnerClassTests extends GroovyCompilerTestSuite { @@ -213,7 +214,7 @@ public void testInnerClass2() { } @Test - public void testInnerClass2a() { + public void testInnerClass3() { //@formatter:off String[] sources = { "Outer.groovy", @@ -234,7 +235,7 @@ public void testInnerClass2a() { } @Test - public void testInnerClass3() { + public void testInnerClass4() { //@formatter:off String[] sources = { "WithInnerClass.groovy", @@ -253,8 +254,28 @@ public void testInnerClass3() { runNegativeTest(sources, ""); } + @Test // GROOVY-4287 + public void testInnerClass5() { + //@formatter:off + String[] sources = { + "Script.groovy", + "import static p.Outer.Inner\n" + + "new Inner()\n", + + "p/Outer.groovy", + "package p\n" + + "class Outer {\n" + + " static class Inner {\n" + + " }\n" + + "}\n", + }; + //@formatter:on + + runConformTest(sources); + } + @Test // https://github.com/groovy/groovy-eclipse/issues/708 - public void testInnerClass4() { + public void testInnerClass6() { //@formatter:off String[] sources = { "Script.groovy", @@ -281,7 +302,51 @@ public void testInnerClass4() { } @Test - public void testInnerClass5() { + public void testInnerClass7() { + //@formatter:off + String[] sources = { + "Script.groovy", + "class Outer {\n" + + " @groovy.transform.TupleConstructor(defaults=false)\n" + + " class Inner {\n" + + " int p\n" + + " }\n" + + " static m(int n) {\n" + + " new Inner(new Outer(), n)\n" + + " }\n" + + "}\n" + + "print Outer.m(4).p\n" + + "print new Outer.Inner(new Outer(), 2).p\n", + }; + //@formatter:on + + runConformTest(sources, "42"); + } + + @Ignore @Test // GROOVY-8947 + public void testInnerClass8() { + //@formatter:off + String[] sources = { + "Script.groovy", + "class Outer {\n" + + " @groovy.transform.TupleConstructor(defaults=false)\n" + + " class Inner {\n" + + " int p\n" + + " }\n" + + " static m(int n) {\n" + + " new Outer().(new Inner(n))\n" + // TODO: no parens + " }\n" + + "}\n" + + "print Outer.m(4).p\n" + + "print new Outer().(new Outer.Inner(2)).p\n", // TODO: no parens, no qualifier + }; + //@formatter:on + + runConformTest(sources, "42"); + } + + @Test + public void testInnerClass9() { //@formatter:off String[] sources = { "Script.groovy", @@ -1595,6 +1660,25 @@ public void testAnonymousInnerClass32() { runConformTest(sources); } + @Test // GROOVY-6977 + public void testAnonymousInnerClass33() { + //@formatter:off + String[] sources = { + "Script.groovy", + "class C {\n" + + " def List foo() {\n" + + " new ArrayList() {}\n" + + " }\n" + + "}\n" + + "def longList = new C().foo()\n" + + "assert longList != null\n" + + "assert longList.empty\n", + }; + //@formatter:on + + runConformTest(sources); + } + @Test public void testMixedModeInnerProperties_GRE597() { //@formatter:off diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/control/CompilationUnit.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/control/CompilationUnit.java index c117d3ee2d..3d4a9cc231 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/control/CompilationUnit.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/control/CompilationUnit.java @@ -36,7 +36,6 @@ import org.codehaus.groovy.classgen.GeneratorContext; import org.codehaus.groovy.classgen.InnerClassCompletionVisitor; import org.codehaus.groovy.classgen.InnerClassVisitor; -import org.codehaus.groovy.classgen.VariableScopeVisitor; import org.codehaus.groovy.classgen.Verifier; import org.codehaus.groovy.control.customizers.CompilationCustomizer; import org.codehaus.groovy.control.io.InputStreamReaderSource; @@ -582,12 +581,12 @@ public void compile(int throughPhase) throws CompilationFailedException { throughPhase = Math.min(throughPhase, Phases.ALL); while (throughPhase >= phase && phase <= Phases.ALL) { - + /* GRECLIPSE edit -- GROOVY-4386, et al. if (phase == Phases.SEMANTIC_ANALYSIS) { doPhaseOperation(resolve); if (dequeued()) continue; } - + */ processPhaseOperations(phase); // Grab processing may have brought in new AST transforms into various phases, process them as well processNewPhaseOperations(phase); @@ -689,9 +688,10 @@ protected boolean dequeued() throws CompilationFailedException { public void call(SourceUnit source) throws CompilationFailedException { List classes = source.ast.getClasses(); for (ClassNode node : classes) { + /* GRECLIPSE edit -- GROOVY-4386, et al. VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(source); scopeVisitor.visitClass(node); - + */ resolveVisitor.setClassNodeResolver(classNodeResolver); resolveVisitor.startResolving(node, source); } diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/control/ResolveVisitor.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/control/ResolveVisitor.java index 010ab3baba..5bd780c93c 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/control/ResolveVisitor.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/control/ResolveVisitor.java @@ -61,6 +61,7 @@ import org.codehaus.groovy.ast.stmt.CatchStatement; import org.codehaus.groovy.ast.stmt.ForStatement; import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.classgen.VariableScopeVisitor; import org.codehaus.groovy.control.ClassNodeResolver.LookupResult; import org.codehaus.groovy.syntax.Types; import org.codehaus.groovy.transform.trait.Traits; @@ -70,6 +71,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -1330,10 +1332,15 @@ protected Expression transformClosureExpression(ClosureExpression ce) { } protected Expression transformConstructorCallExpression(ConstructorCallExpression cce) { + /* GRECLIPSE edit -- GROOVY-4386, et al. ClassNode type = cce.getType(); if (cce.isUsingAnonymousInnerClass()) { // GROOVY-9642 resolveOrFail(type.getUnresolvedSuperClass(false), type); } else { + */ + if (!cce.isUsingAnonymousInnerClass()) { + ClassNode type = cce.getType(); + // GRECLIPSE end resolveOrFail(type, cce); if (type.isAbstract()) { addError("You cannot create an instance from the abstract " + getDescription(type) + ".", cce); @@ -1506,7 +1513,7 @@ public void visitClass(ClassNode node) { if (Modifier.isStatic(node.getModifiers())) { genericParameterNames = new HashMap(); } - + /* GRECLIPSE edit -- GROOVY-4386, et al. InnerClassNode innerClassNode = (InnerClassNode) node; if (innerClassNode.isAnonymous()) { MethodNode enclosingMethod = innerClassNode.getEnclosingMethod(); @@ -1514,6 +1521,7 @@ public void visitClass(ClassNode node) { resolveGenericsHeader(enclosingMethod.getGenericsTypes()); } } + */ } else { genericParameterNames = new HashMap(); } @@ -1609,6 +1617,19 @@ public void visitClass(ClassNode node) { } } } + // VariableScopeVisitor visits anon. inner class body inline, so resolve now + for (Iterator it = node.getInnerClasses(); it.hasNext(); ) { + InnerClassNode cn = it.next(); + if (cn.isAnonymous()) { + MethodNode enclosingMethod = cn.getEnclosingMethod(); + if (enclosingMethod != null) { + resolveGenericsHeader(enclosingMethod.getGenericsTypes()); // GROOVY-6977 + } + resolveOrFail(cn.getUnresolvedSuperClass(false), cn); // GROOVY-9642 + } + } + // initialize scopes/variables now that imports and super types are resolved + new VariableScopeVisitor(source).visitClass(node); // GRECLIPSE end super.visitClass(node); diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/control/CompilationUnit.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/control/CompilationUnit.java index 2fb3782714..fef34ffc44 100644 --- a/base/org.codehaus.groovy30/src/org/codehaus/groovy/control/CompilationUnit.java +++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/control/CompilationUnit.java @@ -39,7 +39,6 @@ import org.codehaus.groovy.classgen.GeneratorContext; import org.codehaus.groovy.classgen.InnerClassCompletionVisitor; import org.codehaus.groovy.classgen.InnerClassVisitor; -import org.codehaus.groovy.classgen.VariableScopeVisitor; import org.codehaus.groovy.classgen.Verifier; import org.codehaus.groovy.control.customizers.CompilationCustomizer; import org.codehaus.groovy.control.io.InputStreamReaderSource; @@ -629,25 +628,21 @@ public void compile(int throughPhase) throws CompilationFailedException { throughPhase = Math.min(throughPhase, Phases.ALL); while (throughPhase >= phase && phase <= Phases.ALL) { - // GRECLIPSE add - if (phase == Phases.CONVERSION) { - if (sources.size() > 1 && Boolean.TRUE.equals(configuration.getOptimizationOptions().get(CompilerConfiguration.PARALLEL_PARSE))) { - sources.values().parallelStream().forEach(SourceUnit::buildAST); - } else { - sources.values().forEach(SourceUnit::buildAST); - } - } else - // GRECLIPSE end + /* GRECLIPSE edit -- GROOVY-4386, et al. if (phase == Phases.SEMANTIC_ANALYSIS) { resolve.doPhaseOperation(this); if (dequeued()) continue; } - /* GRECLIPSE edit if (phase == Phases.CONVERSION) { buildASTs(); } */ - + if (phase == Phases.CONVERSION) { + for (SourceUnit source : sources.values()) { + source.buildAST(); + } + } + // GRECLIPSE end processPhaseOperations(phase); // Grab processing may have brought in new AST transforms into various phases, process them as well processNewPhaseOperations(phase); @@ -749,9 +744,10 @@ protected boolean dequeued() throws CompilationFailedException { */ private final ISourceUnitOperation resolve = (final SourceUnit source) -> { for (ClassNode classNode : source.getAST().getClasses()) { + /* GRECLIPSE edit -- GROOVY-4386, et al. GroovyClassVisitor visitor = new VariableScopeVisitor(source); visitor.visitClass(classNode); - + */ resolveVisitor.setClassNodeResolver(classNodeResolver); resolveVisitor.startResolving(classNode, source); } diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/control/ResolveVisitor.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/control/ResolveVisitor.java index 2179fe3106..ca547af4bd 100644 --- a/base/org.codehaus.groovy30/src/org/codehaus/groovy/control/ResolveVisitor.java +++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/control/ResolveVisitor.java @@ -41,7 +41,6 @@ import org.codehaus.groovy.ast.Variable; import org.codehaus.groovy.ast.VariableScope; import org.codehaus.groovy.ast.expr.AnnotationConstantExpression; -import org.codehaus.groovy.ast.expr.ArgumentListExpression; import org.codehaus.groovy.ast.expr.BinaryExpression; import org.codehaus.groovy.ast.expr.CastExpression; import org.codehaus.groovy.ast.expr.ClassExpression; @@ -61,6 +60,7 @@ import org.codehaus.groovy.ast.stmt.CatchStatement; import org.codehaus.groovy.ast.stmt.ForStatement; import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.classgen.VariableScopeVisitor; import org.codehaus.groovy.control.ClassNodeResolver.LookupResult; import org.codehaus.groovy.syntax.Types; import org.codehaus.groovy.transform.trait.Traits; @@ -70,6 +70,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -110,7 +111,6 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer { private boolean inPropertyExpression = false; private boolean inClosure = false; - private final Map possibleOuterClassNodeMap = new HashMap<>(); private Map genericParameterNames = new HashMap<>(); private final Set fieldTypesChecked = new HashSet<>(); // GRECLIPSE add @@ -439,12 +439,6 @@ protected boolean resolveNestedClass(final ClassNode type) { if (setRedirect(type, classToCheck)) return true; } - // GROOVY-8947: Resolve non-static inner class outside of outer class. - ClassNode possibleOuterClassNode = possibleOuterClassNodeMap.get(type); - if (possibleOuterClassNode != null) { - if (setRedirect(type, possibleOuterClassNode)) return true; - } - // Another case we want to check here is if we are in a // nested class A$B$C and want to access B without // qualifying it by A.B. A alone will work, since that @@ -1211,12 +1205,15 @@ protected Expression transformClosureExpression(final ClosureExpression ce) { } protected Expression transformConstructorCallExpression(final ConstructorCallExpression cce) { - findPossibleOuterClassNodeForNonStaticInnerClassInstantiation(cce); - + /* GRECLIPSE edit -- GROOVY-4386, et al. ClassNode type = cce.getType(); if (cce.isUsingAnonymousInnerClass()) { // GROOVY-9642 resolveOrFail(type.getUnresolvedSuperClass(false), type); } else { + */ + if (!cce.isUsingAnonymousInnerClass()) { + ClassNode type = cce.getType(); + // GRECLIPSE end resolveOrFail(type, cce); if (type.isAbstract()) { addError("You cannot create an instance from the abstract " + getDescription(type) + ".", cce); @@ -1226,28 +1223,6 @@ protected Expression transformConstructorCallExpression(final ConstructorCallExp return cce.transformExpression(this); } - private void findPossibleOuterClassNodeForNonStaticInnerClassInstantiation(final ConstructorCallExpression cce) { - // GROOVY-8947: Fail to resolve non-static inner class outside of outer class - // `new Computer().new Cpu(4)` will be parsed to `new Cpu(new Computer(), 4)` - // so non-static inner class instantiation expression's first argument is a constructor call of outer class - // but the first argument is constructor call can not be non-static inner class instantiation expression, e.g. - // `new HashSet(new ArrayList())`, so we add "possible" to the variable name - Expression argumentExpression = cce.getArguments(); - if (argumentExpression instanceof ArgumentListExpression) { - ArgumentListExpression argumentListExpression = (ArgumentListExpression) argumentExpression; - List expressionList = argumentListExpression.getExpressions(); - if (!expressionList.isEmpty()) { - Expression firstExpression = expressionList.get(0); - - if (firstExpression instanceof ConstructorCallExpression) { - ConstructorCallExpression constructorCallExpression = (ConstructorCallExpression) firstExpression; - ClassNode possibleOuterClassNode = constructorCallExpression.getType(); - possibleOuterClassNodeMap.put(cce.getType(), possibleOuterClassNode); - } - } - } - } - private static String getDescription(final ClassNode node) { return (node.isInterface() ? "interface" : "class") + " '" + node.getName() + "'"; } @@ -1407,7 +1382,7 @@ public void visitClass(final ClassNode node) { if (Modifier.isStatic(node.getModifiers())) { genericParameterNames = new HashMap<>(); } - + /* GRECLIPSE edit -- GROOVY-4386, et al. InnerClassNode innerClassNode = (InnerClassNode) node; if (innerClassNode.isAnonymous()) { MethodNode enclosingMethod = innerClassNode.getEnclosingMethod(); @@ -1415,6 +1390,7 @@ public void visitClass(final ClassNode node) { resolveGenericsHeader(enclosingMethod.getGenericsTypes()); } } + */ } else { genericParameterNames = new HashMap<>(); } @@ -1495,6 +1471,19 @@ public void visitClass(final ClassNode node) { } } } + // VariableScopeVisitor visits anon. inner class body inline, so resolve now + for (Iterator it = node.getInnerClasses(); it.hasNext(); ) { + InnerClassNode cn = it.next(); + if (cn.isAnonymous()) { + MethodNode enclosingMethod = cn.getEnclosingMethod(); + if (enclosingMethod != null) { + resolveGenericsHeader(enclosingMethod.getGenericsTypes()); // GROOVY-6977 + } + resolveOrFail(cn.getUnresolvedSuperClass(false), cn); // GROOVY-9642 + } + } + // initialize scopes/variables now that imports and super types are resolved + new VariableScopeVisitor(source).visitClass(node); // GRECLIPSE end super.visitClass(node); diff --git a/base/org.codehaus.groovy40/src/org/codehaus/groovy/control/CompilationUnit.java b/base/org.codehaus.groovy40/src/org/codehaus/groovy/control/CompilationUnit.java index 211b3eb36f..6f5165e07b 100644 --- a/base/org.codehaus.groovy40/src/org/codehaus/groovy/control/CompilationUnit.java +++ b/base/org.codehaus.groovy40/src/org/codehaus/groovy/control/CompilationUnit.java @@ -39,7 +39,6 @@ import org.codehaus.groovy.classgen.GeneratorContext; import org.codehaus.groovy.classgen.InnerClassCompletionVisitor; import org.codehaus.groovy.classgen.InnerClassVisitor; -import org.codehaus.groovy.classgen.VariableScopeVisitor; import org.codehaus.groovy.classgen.Verifier; import org.codehaus.groovy.control.customizers.CompilationCustomizer; import org.codehaus.groovy.control.io.InputStreamReaderSource; @@ -629,6 +628,7 @@ public void compile(int throughPhase) throws CompilationFailedException { throughPhase = Math.min(throughPhase, Phases.ALL); while (throughPhase >= phase && phase <= Phases.ALL) { + /* GRECLIPSE edit -- GROOVY-4386, et al. if (phase == Phases.SEMANTIC_ANALYSIS) { resolve.doPhaseOperation(this); if (dequeued()) continue; @@ -637,7 +637,13 @@ public void compile(int throughPhase) throws CompilationFailedException { ? sources.values().parallelStream() : sources.values().stream() ).forEach(SourceUnit::buildAST); } - + */ + if (phase == Phases.CONVERSION) { + for (SourceUnit source : sources.values()) { + source.buildAST(); + } + } + // GRECLIPSE end processPhaseOperations(phase); // Grab processing may have brought in new AST transforms into various phases, process them as well processNewPhaseOperations(phase); @@ -724,9 +730,10 @@ protected boolean dequeued() throws CompilationFailedException { */ private final ISourceUnitOperation resolve = (final SourceUnit source) -> { for (ClassNode classNode : source.getAST().getClasses()) { + /* GRECLIPSE edit -- GROOVY-4386, et al. GroovyClassVisitor visitor = new VariableScopeVisitor(source); visitor.visitClass(classNode); - + */ resolveVisitor.setClassNodeResolver(classNodeResolver); resolveVisitor.startResolving(classNode, source); } diff --git a/base/org.codehaus.groovy40/src/org/codehaus/groovy/control/ResolveVisitor.java b/base/org.codehaus.groovy40/src/org/codehaus/groovy/control/ResolveVisitor.java index 0aa19a8e80..04138438a7 100644 --- a/base/org.codehaus.groovy40/src/org/codehaus/groovy/control/ResolveVisitor.java +++ b/base/org.codehaus.groovy40/src/org/codehaus/groovy/control/ResolveVisitor.java @@ -41,7 +41,6 @@ import org.codehaus.groovy.ast.Variable; import org.codehaus.groovy.ast.VariableScope; import org.codehaus.groovy.ast.expr.AnnotationConstantExpression; -import org.codehaus.groovy.ast.expr.ArgumentListExpression; import org.codehaus.groovy.ast.expr.BinaryExpression; import org.codehaus.groovy.ast.expr.CastExpression; import org.codehaus.groovy.ast.expr.ClassExpression; @@ -61,6 +60,7 @@ import org.codehaus.groovy.ast.stmt.CatchStatement; import org.codehaus.groovy.ast.stmt.ForStatement; import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.classgen.VariableScopeVisitor; import org.codehaus.groovy.control.ClassNodeResolver.LookupResult; import org.codehaus.groovy.syntax.Types; import org.codehaus.groovy.transform.trait.Traits; @@ -70,6 +70,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; @@ -109,7 +110,6 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer { private boolean inPropertyExpression = false; private boolean inClosure = false; - private final Map possibleOuterClassNodeMap = new HashMap<>(); private Map genericParameterNames = new HashMap<>(); private final Set fieldTypesChecked = new HashSet<>(); // GRECLIPSE add @@ -436,10 +436,6 @@ protected boolean resolveNestedClass(final ClassNode type) { // GROOVY-9866: unresolvable interfaces } - // GROOVY-8947: non-static inner class outside of outer class - cn = possibleOuterClassNodeMap.get(type); - if (cn != null && setRedirect(type, cn)) return true; - // Another case we want to check here is if we are in a // nested class A$B$C and want to access B without // qualifying it by A.B. A alone will work, since that @@ -1206,12 +1202,15 @@ protected Expression transformClosureExpression(final ClosureExpression ce) { } protected Expression transformConstructorCallExpression(final ConstructorCallExpression cce) { - findPossibleOuterClassNodeForNonStaticInnerClassInstantiation(cce); - + /* GRECLIPSE edit -- GROOVY-4386, et al. ClassNode type = cce.getType(); if (cce.isUsingAnonymousInnerClass()) { // GROOVY-9642 resolveOrFail(type.getUnresolvedSuperClass(false), type); } else { + */ + if (!cce.isUsingAnonymousInnerClass()) { + ClassNode type = cce.getType(); + // GRECLIPSE end resolveOrFail(type, cce); if (type.isAbstract()) { addError("You cannot create an instance from the abstract " + getDescription(type) + ".", cce); @@ -1221,28 +1220,6 @@ protected Expression transformConstructorCallExpression(final ConstructorCallExp return cce.transformExpression(this); } - private void findPossibleOuterClassNodeForNonStaticInnerClassInstantiation(final ConstructorCallExpression cce) { - // GROOVY-8947: Fail to resolve non-static inner class outside of outer class - // `new Computer().new Cpu(4)` will be parsed to `new Cpu(new Computer(), 4)` - // so non-static inner class instantiation expression's first argument is a constructor call of outer class - // but the first argument is constructor call can not be non-static inner class instantiation expression, e.g. - // `new HashSet(new ArrayList())`, so we add "possible" to the variable name - Expression argumentExpression = cce.getArguments(); - if (argumentExpression instanceof ArgumentListExpression) { - ArgumentListExpression argumentListExpression = (ArgumentListExpression) argumentExpression; - List expressionList = argumentListExpression.getExpressions(); - if (!expressionList.isEmpty()) { - Expression firstExpression = expressionList.get(0); - - if (firstExpression instanceof ConstructorCallExpression) { - ConstructorCallExpression constructorCallExpression = (ConstructorCallExpression) firstExpression; - ClassNode possibleOuterClassNode = constructorCallExpression.getType(); - possibleOuterClassNodeMap.put(cce.getType(), possibleOuterClassNode); - } - } - } - } - private static String getDescription(final ClassNode node) { return (node.isInterface() ? "interface" : "class") + " '" + node.getName() + "'"; } @@ -1402,7 +1379,7 @@ public void visitClass(final ClassNode node) { if (Modifier.isStatic(node.getModifiers())) { genericParameterNames = new HashMap<>(); } - + /* GRECLIPSE edit -- GROOVY-4386, et al. InnerClassNode innerClassNode = (InnerClassNode) node; if (innerClassNode.isAnonymous()) { MethodNode enclosingMethod = innerClassNode.getEnclosingMethod(); @@ -1410,6 +1387,7 @@ public void visitClass(final ClassNode node) { resolveGenericsHeader(enclosingMethod.getGenericsTypes()); } } + */ } else { genericParameterNames = new HashMap<>(); } @@ -1490,6 +1468,19 @@ public void visitClass(final ClassNode node) { } } } + // VariableScopeVisitor visits anon. inner class body inline, so resolve now + for (Iterator it = node.getInnerClasses(); it.hasNext(); ) { + InnerClassNode cn = it.next(); + if (cn.isAnonymous()) { + MethodNode enclosingMethod = cn.getEnclosingMethod(); + if (enclosingMethod != null) { + resolveGenericsHeader(enclosingMethod.getGenericsTypes()); // GROOVY-6977 + } + resolveOrFail(cn.getUnresolvedSuperClass(false), cn); // GROOVY-9642 + } + } + // initialize scopes/variables now that imports and super types are resolved + new VariableScopeVisitor(source).visitClass(node); // GRECLIPSE end super.visitClass(node);