diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/locations/SourceLocationsTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/locations/SourceLocationsTests.java index 5587666cea..03c1e6ab29 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/locations/SourceLocationsTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/locations/SourceLocationsTests.java @@ -39,6 +39,7 @@ import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.Initializer; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; @@ -127,6 +128,9 @@ private static void assertUnitWithSingleType(String source, ICompilationUnit uni private static void assertDeclaration(IMember decl, BodyDeclaration body, int memberNumber, String source) throws Exception { char astKind; switch (decl.getElementType()) { + case IJavaElement.INITIALIZER: + astKind = 'i'; + break; case IJavaElement.METHOD: astKind = 'm'; break; @@ -148,10 +152,14 @@ private static void assertDeclaration(IMember decl, BodyDeclaration body, int me String endTag = "/*" + astKind + memberNumber + "e*/"; int len = (isParrotParser() || (decl instanceof IMethod && - decl.getNameRange().getLength() > 0 && !Flags.isAbstract(((IMethod) decl).getFlags())) ? 0 : endTag.length()); + decl.getNameRange().getLength() > 0 && !Flags.isAbstract(decl.getFlags())) ? 0 : endTag.length()); int end = source.indexOf(endTag) + len; if (len == 0 && source.substring(0, end).endsWith("*/")) { end = source.substring(0, end).lastIndexOf("/*"); + } else if (len > 0) { + while (source.substring(end).startsWith("/*")) { + end = source.indexOf("*/", end) + 2; + } } ISourceRange declRange = decl.getSourceRange(); @@ -205,10 +213,11 @@ private static void assertDeclaration(IMember decl, BodyDeclaration body, int me nameEnd += nameEndTag.length(); } - ISourceRange nameDeclRange = decl.getNameRange(); - assertEquals(decl + "\nhas incorrect name start value", nameStart, nameDeclRange.getOffset()); - assertEquals(decl + "\nhas incorrect name end value", nameEnd, nameDeclRange.getOffset() + nameDeclRange.getLength()); - + if (!(body instanceof Initializer)) { + ISourceRange nameRange = decl.getNameRange(); + assertEquals(decl + "\nhas incorrect name start value", nameStart, nameRange.getOffset()); + assertEquals(decl + "\nhas incorrect name end value", nameEnd, nameRange.getOffset() + nameRange.getLength()); + } if (body instanceof FieldDeclaration) { SimpleName name = ((VariableDeclarationFragment) ((FieldDeclaration) body).fragments().get(0)).getName(); assertEquals(body + "\nhas incorrect source start value", nameStart, name.getStartPosition()); @@ -623,13 +632,30 @@ public void testSourceLocationsEnumDeclaration() throws Exception { } @Test - public void testSourceLocationsObjectInitializers() throws Exception { + public void testSourceLocationsObjectInitializers1() throws Exception { String source = "package p1\n" + "/*t0s*/class /*t0sn*/Hello/*t0en*/ /*t0sb*/{\n" + - " /*m0s*//*m0sb*/{\n" + + " /*i0s*//*m1s*//*m1sb*/{\n" + " int i = 0\n" + - " }/*m0e*/\n" + + " }/*i0e*//*m1e*/\n" + + "}/*t0e*/\n"; + assertUnitWithSingleType(source, createCompUnit("p1", "Hello", source)); + } + + @Test + public void testSourceLocationsObjectInitializers2() throws Exception { + String source = + "package p1\n" + + "/*t0s*/class /*t0sn*/Hello/*t0en*/ /*t0sb*/{\n" + + " /*i0s*//*m1s*//*m1sb*/{\n" + + " int i = 0\n" + + " }/*i0e*/\n" + + " /*i2s*/{\n" + + " int j = 1\n" + + " }/*i2e*/\n" + + " /*i3s*/{\n" + + " }/*i3e*//*m1e*/\n" + "}/*t0e*/\n"; assertUnitWithSingleType(source, createCompUnit("p1", "Hello", source)); } diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/InferencingTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/InferencingTests.java index b396fa1b58..11ac8e2687 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/InferencingTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/InferencingTests.java @@ -1406,11 +1406,52 @@ public void testSuperClassMethod5b() { assertExprType(contents, "super", "groovy.lang.GroovyObject"); } + @Test + public void testClassWithInitializer1() { + String contents = + "class A {\n" + + " static int x\n" + + " static {\n" + + " x = 42\n" + + " }\n" + + "}\n"; + int offset = contents.lastIndexOf('x'); + assertType(contents, offset, offset + 1, "java.lang.Integer"); + } + + @Test + public void testClassWithInitializer2() { + String contents = + "class A {\n" + + " int x\n" + + " {\n" + + " x = 42\n" + + " }\n" + + "}\n"; + int offset = contents.lastIndexOf('x'); + assertType(contents, offset, offset + 1, "java.lang.Integer"); + } + + @Test + public void testClassWithInitializer3() { + String contents = + "class A {\n" + + " int x\n" + + "}\n" + + "new A() {\n" + + " {\n" + + " x = 42\n" + + " }\n" + + "}\n"; + int offset = contents.lastIndexOf('x'); + assertType(contents, offset, offset + 1, "java.lang.Integer"); + } + @Test public void testFieldWithInitializer1() { String contents = "class A {\n" + - " def x = 9\n" + + " def x = 42\n" + "}\n" + "new A().x"; int offset = contents.lastIndexOf('x'); @@ -1419,7 +1460,7 @@ public void testFieldWithInitializer1() { @Test public void testFieldWithInitializer2() { - createUnit("A", "class A {\ndef x = 9\n}"); + createUnit("A", "class A {\ndef x = 42\n}"); String contents = "new A().x"; int offset = contents.lastIndexOf('x'); assertType(contents, offset, offset + 1, "java.lang.Integer"); 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 ff61d5ec17..b90eaad0b0 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 @@ -547,6 +547,8 @@ public void testAnonymousInnerClass9a() { checkGCUDeclaration("A.groovy", "public class A {\n" + + " {\n" + + " }\n" + " public " + (isAtLeastGroovy(25) ? "@groovy.transform.Generated " : "") + "A() {\n" + " new Runnable() {\n" + " x() {\n" + @@ -557,6 +559,31 @@ public void testAnonymousInnerClass9a() { "}"); } + @Test + public void testAnonymousInnerClass9b() { + //@formatter:off + String[] sources = { + "Script.groovy", + "class A {}\n" + + "new A() {\n" + + " {\n" + + " def foo = new Runnable() {\n" + + " }\n" + + " }\n" + + "}\n", + }; + //@formatter:on + + runNegativeTest(sources, + "----------\n" + + "1. ERROR in Script.groovy (at line 4)\n" + + "\tdef foo = new Runnable() {\n" + + "\t ^^^^^^^^^^\n" + + "Groovy:Can't have an abstract method in a non-abstract class." + + " The class 'Script$1$1' must be declared abstract or the method 'void run()' must be implemented.\n" + + "----------\n"); + } + @Test // https://github.com/groovy/groovy-eclipse/issues/715 public void testAnonymousInnerClass10() { //@formatter:off diff --git a/base/org.codehaus.groovy24/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java b/base/org.codehaus.groovy24/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java index cc55dff589..03b78eb6a7 100644 --- a/base/org.codehaus.groovy24/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java +++ b/base/org.codehaus.groovy24/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java @@ -182,7 +182,9 @@ public void setLastLineNumber(final int lineNumber) { private MethodNode methodNode; // GRECLIPSE private->protected protected String[] tokenNames; + /* GRECLIPSE edit -- GROOVY-9203 private int innerClassCounter = 1; + */ private boolean enumConstantBeingDef = false; private boolean forStatementBeingDef = false; private boolean firstParamIsVarArg = false; @@ -333,11 +335,6 @@ public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduc setClassLoader(classLoader); makeModule(); try { - // GRECLIPSE add - // just in case buildAST is called twice - innerClassCounter = 1; - // GRECLIPSE end - convertGroovy(ast); // GRECLIPSE add -- does it look broken (i.e. have we built a script for it containing rubbish) @@ -666,10 +663,14 @@ protected void annotationDef(AST classDef) { } protected void interfaceDef(AST classDef) { + /* GRECLIPSE edit -- GROOVY-9203 int oldInnerClassCounter = innerClassCounter; + */ innerInterfaceDef(classDef); classNode = null; + /* GRECLIPSE edit -- GROOVY-9203 innerClassCounter = oldInnerClassCounter; + */ } protected void innerInterfaceDef(AST classDef) { @@ -723,21 +724,29 @@ protected void innerInterfaceDef(AST classDef) { classNode.setNameEnd(nameEnd - 1); // GRECLIPSE end + /* GRECLIPSE edit -- GROOVY-9203 int oldClassCount = innerClassCounter; + */ assertNodeType(OBJBLOCK, node); objectBlock(node); output.addClass(classNode); classNode = outerClass; + /* GRECLIPSE edit -- GROOVY-9203 innerClassCounter = oldClassCount; + */ } protected void classDef(AST classDef) { + /* GRECLIPSE edit -- GROOVY-9203 int oldInnerClassCounter = innerClassCounter; + */ innerClassDef(classDef); classNode = null; + /* GRECLIPSE edit -- GROOVY-9203 innerClassCounter = oldInnerClassCounter; + */ } private ClassNode getClassOrScript(ClassNode node) { @@ -745,11 +754,28 @@ private ClassNode getClassOrScript(ClassNode node) { return output.getScriptClassDummy(); } + // GRECLIPSE add + private static int anonymousClassCount(ClassNode node) { + int count = 0; + for (Iterator it = node.getInnerClasses(); it.hasNext();) { + InnerClassNode innerClass = it.next(); + if (innerClass.isAnonymous()) { + count += 1; + } + } + return count; + } + // GRECLIPSE end + protected Expression anonymousInnerClassDef(AST node) { ClassNode oldNode = classNode; ClassNode outerClass = getClassOrScript(oldNode); + /* GRECLIPSE edit -- GROOVY-9203 String fullName = outerClass.getName() + '$' + innerClassCounter; innerClassCounter++; + */ + String fullName = outerClass.getName() + '$' + (anonymousClassCount(outerClass) + 1); + // GRECLIPSE end if (enumConstantBeingDef) { classNode = new EnumConstantClassNode(outerClass, fullName, Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE); } else { @@ -845,7 +871,9 @@ protected void innerClassDef(AST classDef) { // have here to ensure it won't be the inner class output.addClass(classNode); + /* GRECLIPSE edit -- GROOVY-9203 int oldClassCount = innerClassCounter; + */ // GRECLIPSE add // a null node means the classbody is missing but the parser recovered @@ -859,7 +887,9 @@ protected void innerClassDef(AST classDef) { // GRECLIPSE end classNode = outerClass; + /* GRECLIPSE edit -- GROOVY-9203 innerClassCounter = oldClassCount; + */ } protected void objectBlock(AST objectBlock) { diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java index 65ab75abc2..a2f4b5c216 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java @@ -182,7 +182,9 @@ public void setLastLineNumber(final int lineNumber) { private MethodNode methodNode; // GRECLIPSE private->protected protected String[] tokenNames; + /* GRECLIPSE edit -- GROOVY-9203 private int innerClassCounter = 1; + */ private boolean enumConstantBeingDef = false; private boolean forStatementBeingDef = false; private boolean annotationBeingDef = false; @@ -334,11 +336,8 @@ public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduc setClassLoader(classLoader); makeModule(); try { - // GRECLIPSE add - // just in case buildAST is called twice - innerClassCounter = 1; - // GRECLIPSE end convertGroovy(ast); + // GRECLIPSE add -- does it look broken (i.e. have we built a script for it containing rubbish) if (looksBroken(output) && output.getMethods().isEmpty() && sourceUnit.getErrorCollector().hasErrors()) { output.setEncounteredUnrecoverableError(true); @@ -367,6 +366,7 @@ public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduc scriptClassNode.setLastLineNumber(lastStatement.getLastLineNumber()); } } + // GRECLIPSE add fixModuleNodeLocations(); output.putNodeMetaData(LocationSupport.class, locations); @@ -664,10 +664,14 @@ protected void annotationDef(AST classDef) { } protected void interfaceDef(AST classDef) { + /* GRECLIPSE edit -- GROOVY-9203 int oldInnerClassCounter = innerClassCounter; + */ innerInterfaceDef(classDef); classNode = null; + /* GRECLIPSE edit -- GROOVY-9203 innerClassCounter = oldInnerClassCounter; + */ } protected void innerInterfaceDef(AST classDef) { @@ -721,21 +725,29 @@ protected void innerInterfaceDef(AST classDef) { classNode.setNameEnd(nameEnd - 1); // GRECLIPSE end + /* GRECLIPSE edit -- GROOVY-9203 int oldClassCount = innerClassCounter; + */ assertNodeType(OBJBLOCK, node); objectBlock(node); output.addClass(classNode); classNode = outerClass; + /* GRECLIPSE edit -- GROOVY-9203 innerClassCounter = oldClassCount; + */ } protected void classDef(AST classDef) { + /* GRECLIPSE edit -- GROOVY-9203 int oldInnerClassCounter = innerClassCounter; + */ innerClassDef(classDef); classNode = null; + /* GRECLIPSE edit -- GROOVY-9203 innerClassCounter = oldInnerClassCounter; + */ } private ClassNode getClassOrScript(ClassNode node) { @@ -743,11 +755,28 @@ private ClassNode getClassOrScript(ClassNode node) { return output.getScriptClassDummy(); } + // GRECLIPSE add + private static int anonymousClassCount(ClassNode node) { + int count = 0; + for (Iterator it = node.getInnerClasses(); it.hasNext();) { + InnerClassNode innerClass = it.next(); + if (innerClass.isAnonymous()) { + count += 1; + } + } + return count; + } + // GRECLIPSE end + protected Expression anonymousInnerClassDef(AST node) { ClassNode oldNode = classNode; ClassNode outerClass = getClassOrScript(oldNode); + /* GRECLIPSE edit -- GROOVY-9203 String fullName = outerClass.getName() + '$' + innerClassCounter; innerClassCounter++; + */ + String fullName = outerClass.getName() + '$' + (anonymousClassCount(outerClass) + 1); + // GRECLIPSE end if (enumConstantBeingDef) { classNode = new EnumConstantClassNode(outerClass, fullName, Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE); } else { @@ -843,7 +872,9 @@ protected void innerClassDef(AST classDef) { // have here to ensure it won't be the inner class output.addClass(classNode); + /* GRECLIPSE edit -- GROOVY-9203 int oldClassCount = innerClassCounter; + */ // GRECLIPSE add // a null node means the classbody is missing but the parser recovered @@ -857,7 +888,9 @@ protected void innerClassDef(AST classDef) { // GRECLIPSE end classNode = outerClass; + /* GRECLIPSE edit -- GROOVY-9203 innerClassCounter = oldClassCount; + */ } protected void objectBlock(AST objectBlock) { diff --git a/base/org.codehaus.groovy30/src/org/apache/groovy/parser/antlr4/AstBuilder.java b/base/org.codehaus.groovy30/src/org/apache/groovy/parser/antlr4/AstBuilder.java index fc1a13ed00..ecd5d34194 100644 --- a/base/org.codehaus.groovy30/src/org/apache/groovy/parser/antlr4/AstBuilder.java +++ b/base/org.codehaus.groovy30/src/org/apache/groovy/parser/antlr4/AstBuilder.java @@ -134,6 +134,7 @@ import java.util.Collections; import java.util.Deque; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -1515,12 +1516,16 @@ public ClassNode visitClassDeclaration(ClassDeclarationContext ctx) { classNodeList.add(classNode); } + /* GRECLIPSE edit -- GROOVY-9203 int oldAnonymousInnerClassCounter = this.anonymousInnerClassCounter; + */ classNodeStack.push(classNode); ctx.classBody().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode); this.visitClassBody(ctx.classBody()); classNodeStack.pop(); + /* GRECLIPSE edit -- GROOVY-9203 this.anonymousInnerClassCounter = oldAnonymousInnerClassCounter; + */ if (!(asBoolean(ctx.CLASS()) || asBoolean(ctx.TRAIT()))) { classNodeList.add(classNode); @@ -3735,10 +3740,23 @@ private ClassNode createArrayType(ClassNode classNode, List return arrayType; } - + /* GRECLIPSE edit -- GROOVY-9203 private String genAnonymousClassName(String outerClassName) { return outerClassName + "$" + this.anonymousInnerClassCounter++; } + */ + private static String nextAnonymousClassName(ClassNode outerClass) { + int anonymousClassCount = 0; + for (Iterator it = outerClass.getInnerClasses(); it.hasNext();) { + InnerClassNode innerClass = it.next(); + if (innerClass.isAnonymous()) { + anonymousClassCount += 1; + } + } + + return outerClass.getName() + "$" + (anonymousClassCount + 1); + } + // GRECLIPSE end @Override public InnerClassNode visitAnonymousInnerClassDeclaration(AnonymousInnerClassDeclarationContext ctx) { @@ -3750,7 +3768,11 @@ public InnerClassNode visitAnonymousInnerClassDeclaration(AnonymousInnerClassDec ClassNode outerClass = this.classNodeStack.peek(); outerClass = asBoolean(outerClass) ? outerClass : moduleNode.getScriptClassDummy(); + /* GRECLIPSE edit -- GROOVY-9203 String fullName = this.genAnonymousClassName(outerClass.getName()); + */ + String fullName = nextAnonymousClassName(outerClass); + // GRECLIPSE end if (1 == ctx.t) { // anonymous enum anonymousInnerClass = new EnumConstantClassNode(outerClass, fullName, superClass.getModifiers() | Opcodes.ACC_FINAL, superClass.getPlainNodeReference()); @@ -5296,7 +5318,9 @@ public List getDeclarationExpressions() { private final List classNodeList = new LinkedList<>(); private final Deque classNodeStack = new ArrayDeque<>(); private final Deque> anonymousInnerClassesDefinedInMethodStack = new ArrayDeque<>(); + /* GRECLIPSE edit -- GROOVY-9203 private int anonymousInnerClassCounter = 1; + */ private Tuple2 numberFormatError; diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java index a26022def3..514ca623ed 100644 --- a/base/org.codehaus.groovy30/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java +++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java @@ -183,7 +183,9 @@ public void setLastLineNumber(final int lineNumber) { private MethodNode methodNode; // GRECLIPSE private->protected protected String[] tokenNames; + /* GRECLIPSE edit -- GROOVY-9203 private int innerClassCounter = 1; + */ private boolean enumConstantBeingDef = false; private boolean forStatementBeingDef = false; private boolean annotationBeingDef = false; @@ -335,11 +337,6 @@ public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduc setClassLoader(classLoader); makeModule(); try { - // GRECLIPSE add - // just in case buildAST is called twice - innerClassCounter = 1; - // GRECLIPSE end - convertGroovy(ast); // GRECLIPSE add -- does it look broken (i.e. have we built a script for it containing rubbish) @@ -756,10 +753,14 @@ protected void annotationDef(AST classDef) { } protected void interfaceDef(AST classDef) { + /* GRECLIPSE edit -- GROOVY-9203 int oldInnerClassCounter = innerClassCounter; + */ innerInterfaceDef(classDef); classNode = null; + /* GRECLIPSE edit -- GROOVY-9203 innerClassCounter = oldInnerClassCounter; + */ } protected void innerInterfaceDef(AST classDef) { @@ -813,21 +814,29 @@ protected void innerInterfaceDef(AST classDef) { classNode.setNameEnd(nameEnd - 1); // GRECLIPSE end + /* GRECLIPSE edit -- GROOVY-9203 int oldClassCount = innerClassCounter; + */ assertNodeType(OBJBLOCK, node); objectBlock(node); output.addClass(classNode); classNode = outerClass; + /* GRECLIPSE edit -- GROOVY-9203 innerClassCounter = oldClassCount; + */ } protected void classDef(AST classDef) { + /* GRECLIPSE edit -- GROOVY-9203 int oldInnerClassCounter = innerClassCounter; + */ innerClassDef(classDef); classNode = null; + /* GRECLIPSE edit -- GROOVY-9203 innerClassCounter = oldInnerClassCounter; + */ } private ClassNode getClassOrScript(ClassNode node) { @@ -835,11 +844,28 @@ private ClassNode getClassOrScript(ClassNode node) { return output.getScriptClassDummy(); } + // GRECLIPSE add + private static int anonymousClassCount(ClassNode node) { + int count = 0; + for (Iterator it = node.getInnerClasses(); it.hasNext();) { + InnerClassNode innerClass = it.next(); + if (innerClass.isAnonymous()) { + count += 1; + } + } + return count; + } + // GRECLIPSE end + protected Expression anonymousInnerClassDef(AST node) { ClassNode oldNode = classNode; ClassNode outerClass = getClassOrScript(oldNode); + /* GRECLIPSE edit -- GROOVY-9203 String fullName = outerClass.getName() + '$' + innerClassCounter; innerClassCounter++; + */ + String fullName = outerClass.getName() + '$' + (anonymousClassCount(outerClass) + 1); + // GRECLIPSE end if (enumConstantBeingDef) { classNode = new EnumConstantClassNode(outerClass, fullName, Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE); } else { @@ -935,7 +961,9 @@ protected void innerClassDef(AST classDef) { // have here to ensure it won't be the inner class output.addClass(classNode); + /* GRECLIPSE edit -- GROOVY-9203 int oldClassCount = innerClassCounter; + */ // GRECLIPSE add // a null node means the classbody is missing but the parser recovered @@ -949,7 +977,9 @@ protected void innerClassDef(AST classDef) { // GRECLIPSE end classNode = outerClass; + /* GRECLIPSE edit -- GROOVY-9203 innerClassCounter = oldClassCount; + */ } protected void objectBlock(AST objectBlock) { diff --git a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyCompilationUnitDeclaration.java b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyCompilationUnitDeclaration.java index a1deb0aa12..a42e849911 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyCompilationUnitDeclaration.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyCompilationUnitDeclaration.java @@ -110,6 +110,7 @@ import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference; +import org.eclipse.jdt.internal.compiler.ast.Block; import org.eclipse.jdt.internal.compiler.ast.CastExpression; import org.eclipse.jdt.internal.compiler.ast.CharLiteral; import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess; @@ -121,6 +122,7 @@ import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.FloatLiteral; import org.eclipse.jdt.internal.compiler.ast.ImportReference; +import org.eclipse.jdt.internal.compiler.ast.Initializer; import org.eclipse.jdt.internal.compiler.ast.IntLiteral; import org.eclipse.jdt.internal.compiler.ast.Javadoc; import org.eclipse.jdt.internal.compiler.ast.Literal; @@ -1137,8 +1139,17 @@ private void createTypeDeclarations(ModuleNode moduleNode) { typeDeclaration.methods = createConstructorAndMethodDeclarations(classNode, isEnum, typeDeclaration); for (Statement statement : classNode.getObjectInitializerStatements()) { - if (statement.getEnd() > 0 && typeDeclaration.methods.length > 0) - statement.visit(new AnonInnerFinder(typeDeclaration.methods[0])); + if (statement.getEnd() > 0) { + Initializer initializer = new Initializer(new Block(0), Flags.AccDefault); + initializer.declarationSourceEnd = initializer.sourceEnd = statement.getEnd() - 1; + initializer.declarationSourceStart = initializer.sourceStart = statement.getStart(); + typeDeclaration.fields = (FieldDeclaration[]) ArrayUtils.add(typeDeclaration.fields, initializer); + + if (anonymousLocations != null) { + statement.visit(new AnonInnerFinder(typeDeclaration.methods.length > 0 && + typeDeclaration.methods[0].isConstructor() ? typeDeclaration.methods[0] : initializer)); + } + } } if (isInner) { @@ -1186,6 +1197,11 @@ private void createTypeDeclarations(ModuleNode moduleNode) { methodDeclaration.bits |= ASTNode.HasLocalType; methodDeclaration.statements = (org.eclipse.jdt.internal.compiler.ast.Statement[]) ArrayUtils.add(methodDeclaration.statements != null ? methodDeclaration.statements : new org.eclipse.jdt.internal.compiler.ast.Statement[0], innerTypeDeclaration.allocation); + } else if (location instanceof Initializer) { + Initializer initializer = (Initializer) location; + initializer.bits |= ASTNode.HasLocalType; + initializer.block.statements = (org.eclipse.jdt.internal.compiler.ast.Statement[]) ArrayUtils.add(initializer.block.statements != null + ? initializer.block.statements : new org.eclipse.jdt.internal.compiler.ast.Statement[0], innerTypeDeclaration.allocation); } else if (location instanceof FieldDeclaration) { FieldDeclarationWithInitializer fieldDeclaration = (FieldDeclarationWithInitializer) location; fieldDeclaration.bits |= ASTNode.HasLocalType; @@ -1319,7 +1335,7 @@ private void createConstructorDeclarations(ClassNode classNode, boolean isEnum, ctorName = classNode.getNameWithoutPackage().toCharArray(); } - // add default constructor if no other constructors exist (and not trait/interface/anonymous) + // add default constructor if no other constructors exist (and not anonymous/interface/trait) if (constructorNodes.isEmpty() && !isAnon && !classNode.isInterface() && !isTrait(classNode)) { ConstructorDeclaration constructorDecl = new ConstructorDeclaration(unitDeclaration.compilationResult); try { diff --git a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyTypeDeclaration.java b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyTypeDeclaration.java index e20bb70347..f0f6a6b09b 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyTypeDeclaration.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyTypeDeclaration.java @@ -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, @@ -20,6 +20,8 @@ import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Initializer; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.Scope; @@ -74,7 +76,13 @@ public void resolve() { method.resolveStatements(); } } - // fields resolved earlier in GroovyClassScope + for (FieldDeclaration field : fields) { + if (field instanceof Initializer) { + field.resolve(field.isStatic() ? staticInitializerScope : initializerScope); + } else { + // fields resolved earlier in GroovyClassScope + } + } } //-------------------------------------------------------------------------- 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 52ef80e3ee..850bdcb75f 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 @@ -19,14 +19,12 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.CancellationException; -import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -199,12 +197,6 @@ private static void log(Throwable t, String form, Object... args) { */ private final LinkedList primaryTypeStack = new LinkedList<>(); - /** - * Tracks the anonymous inner class counts for type members. Required for - * calls to {@code IMember.getType("", occurrenceCount);} - */ - private Map occurrenceCounts = new HashMap<>(); - private final AssignmentStorer assignmentStorer = new AssignmentStorer(); /** @@ -262,7 +254,6 @@ public void visitCompilationUnit(ITypeRequestor requestor) { e.printStackTrace(); } } finally { - occurrenceCounts.clear(); scopes.removeLast(); } if (DEBUG) { @@ -997,11 +988,12 @@ public void visitConstructorCallExpression(ConstructorCallExpression node) { IJavaElement enclosingElement0 = enclosingElement; enclosingDeclarationNode = type; try { - IType anon = findAnonType(type); + IType anon = findAnonType(type, enclosingElement).orElseThrow( + () -> new GroovyEclipseBug("Failed to locate anon. type " + type.getName())); + + enclosingElement = anon; // visit inlined object initializers + type.getDeclaredConstructors().forEach(this::visitMethodInternal); - for (Statement stmt : type.getObjectInitializerStatements()) { - stmt.visit(this); - } for (FieldNode field : type.getFields()) { if (field.getEnd() > 0) { enclosingElement = anon.getField(field.getName()); @@ -1908,30 +1900,30 @@ private boolean handleRequestor(Expression node, ClassNode primaryType, TypeLook // - private IType findAnonType(ClassNode type) { - int occurrenceCount = occurrenceCounts.computeIfAbsent( - enclosingElement, x -> new AtomicInteger()).incrementAndGet(); + private Optional findAnonType(ClassNode type, IJavaElement enclosing) { try { - for (IJavaElement child : ((IMember) enclosingElement).getChildren()) { + for (IJavaElement child : ((IMember) enclosing).getChildren()) { if (child instanceof IType && ((IType) child).isAnonymous() && - ((IType) child).getOccurrenceCount() == occurrenceCount) { - return (IType) child; - } - } - // check for method with AIC in default argument expression - if (enclosingElement instanceof IMethod) { - MethodNode meth = findMethodNode((IMethod) enclosingElement); - if (meth != null && meth.getOriginal() != meth) { - for (IJavaElement elem : occurrenceCounts.keySet()) { - if (elem instanceof IMethod && - elem != enclosingElement && - elem.getElementName().equals(meth.getName())) { - for (IJavaElement child : ((IMember) elem).getChildren()) { - if (child instanceof IType && ((IType) child).isAnonymous() && - ((IType) child).getOccurrenceCount() == occurrenceCount) { - return (IType) child; - } - } + type.getName().endsWith("$" + ((SourceType) child).localOccurrenceCount)) { + return Optional.of((IType) child); + } + } + + if (enclosing instanceof IType) { + for (IJavaElement child : ((IType) enclosing).getChildren()) { + if (child instanceof org.eclipse.jdt.internal.core.Initializer) { + Optional result = findAnonType(type, child); + if (result.isPresent()) return result; + } + } + } else if (enclosing instanceof IMethod) { + // check for method with AIC in default argument expression + MethodNode methodNode = findMethodNode((IMethod) enclosing); + if (methodNode != null && methodNode.getOriginal() != methodNode) { + for (IMethod child : ((IMethod) enclosing).getDeclaringType().getMethods()) { + if (child != enclosing && child.getElementName().equals(methodNode.getName())) { + Optional result = findAnonType(type, child); + if (result.isPresent()) return result; } } } @@ -1939,7 +1931,8 @@ private IType findAnonType(ClassNode type) { } catch (JavaModelException e) { log(e, "Error visiting children of %s", type.getName()); } - throw new GroovyEclipseBug("Failed to locate anon. type " + type.getName()); + + return Optional.empty(); } private ClassNode findClassNode(String name) {