Skip to content

Commit

Permalink
Fix for #926: add object initializer blocks to JDT model and find anon.
Browse files Browse the repository at this point in the history
inner classes there; count anon. inner classes from 1 for each enclosing

GROOVY-9203
  • Loading branch information
eric-milles committed Jul 23, 2019
1 parent cedbbb9 commit 2e19ff2
Show file tree
Hide file tree
Showing 10 changed files with 295 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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" +
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -723,33 +724,58 @@ 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) {
if (node != null) return node;
return output.getScriptClassDummy();
}

// GRECLIPSE add
private static int anonymousClassCount(ClassNode node) {
int count = 0;
for (Iterator<InnerClassNode> 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 {
Expand Down Expand Up @@ -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
Expand All @@ -859,7 +887,9 @@ protected void innerClassDef(AST classDef) {
// GRECLIPSE end

classNode = outerClass;
/* GRECLIPSE edit -- GROOVY-9203
innerClassCounter = oldClassCount;
*/
}

protected void objectBlock(AST objectBlock) {
Expand Down
Loading

0 comments on commit 2e19ff2

Please sign in to comment.