diff --git a/spoon-smpl/src/test/java/spoon/smpl/C4JShouldVibrateTest.java b/spoon-smpl/src/test/java/spoon/smpl/C4JShouldVibrateTest.java index b8ef6f1638a..8663d0dcac9 100644 --- a/spoon-smpl/src/test/java/spoon/smpl/C4JShouldVibrateTest.java +++ b/spoon-smpl/src/test/java/spoon/smpl/C4JShouldVibrateTest.java @@ -63,7 +63,7 @@ public static void initializeContext() { /* 22 */ "...\n" + /* 23 */ "}\n"; - ctx = new ZippedCodeBaseTestContext(smpl, "src/test/resources/C4JShouldVibrate.zip", false); + ctx = new ZippedCodeBaseTestContext(smpl, "src/test/resources/C4JShouldVibrate.zip", true); } @Test diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java index 644a707c4c5..4b13d2ce786 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java @@ -1525,11 +1525,11 @@ public boolean visit(QualifiedNameReference qualifiedNameRef, BlockScope scope) context.enter(factory.Code().createTypeAccessWithoutCloningReference(typeRef), qualifiedNameRef); return true; } else if (qualifiedNameRef.binding instanceof ProblemBinding) { - if (context.stack.peek().element instanceof CtInvocation) { + if (helper.isProblemNameRefProbablyTypeRef(qualifiedNameRef)) { context.enter(helper.createTypeAccessNoClasspath(qualifiedNameRef), qualifiedNameRef); - return true; + } else { + context.enter(helper.createFieldAccessNoClasspath(qualifiedNameRef), qualifiedNameRef); } - context.enter(helper.createFieldAccessNoClasspath(qualifiedNameRef), qualifiedNameRef); return true; } else { context.enter( diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java index f54fbeacbf4..fe89a80b1ba 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java @@ -7,11 +7,13 @@ */ package spoon.support.compiler.jdt; +import java.util.Arrays; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.ExportsStatement; import org.eclipse.jdt.internal.compiler.ast.FieldReference; +import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration; import org.eclipse.jdt.internal.compiler.ast.ModuleReference; import org.eclipse.jdt.internal.compiler.ast.OpensStatement; @@ -392,6 +394,60 @@ CtFieldAccess createFieldAccessNoClasspath(SingleNameReference singleName return va; } + boolean isProblemNameRefProbablyTypeRef(QualifiedNameReference qualifiedNameReference) { + ContextBuilder contextBuilder = jdtTreeBuilder.getContextBuilder(); + if (contextBuilder.compilationunitdeclaration == null) { + return false; + } + if (contextBuilder.compilationunitdeclaration.imports == null) { + return false; + } + char[][] ourName = qualifiedNameReference.tokens; + for (ImportReference anImport : contextBuilder.compilationunitdeclaration.imports) { + char[][] importName = anImport.getImportName(); + int i = indexOfSubList(importName, ourName); + if (i > 0) { + boolean extendsToEndOfImport = i + ourName.length == importName.length; + boolean isStaticImport = anImport.isStatic(); + if (!isStaticImport) { + // import foo.bar.baz.A; => "A" is probably a type + // import foo.bar.baz.A; => "baz" is probably a type + return true; + } + // import static foo.Bar.bar; => bar is probably a method/field + // import static foo.Bar; => Bar is probably a type + char[] simpleName = qualifiedNameReference.tokens[qualifiedNameReference.tokens.length - 1]; + return !extendsToEndOfImport || !Character.isLowerCase(simpleName[0]); + } + } + return false; + } + + /** + * Finds the lowest index where {@code needle} appears in {@code haystack}. This is akin to a + * substring search, but JDT uses a String[] to omit separators and a char[] to represent strings. + * If we want to find out where "String" appears in "java.lang.String", we would call + * {@code indexOfSubList(["java", "lang", "String"], ["lang", "String"])} and receive {@code 1}. + * + * @param haystack the haystack to search in + * @param needle the needle to search + * @return the first index where needle appears in haystack + * @see java.util.Collections#indexOfSubList(List, List) Collections#indexOfSubList for a more + * general version that does not correctly handle array equality + */ + private static int indexOfSubList(char[][] haystack, char[][] needle) { + outer: + for (int i = 0; i < haystack.length - needle.length; i++) { + for (int j = 0; j < needle.length; j++) { + if (!Arrays.equals(haystack[i + j], needle[j])) { + continue outer; + } + } + return i; + } + return -1; + } + /** * In no classpath mode, when we build a field access, we have a binding typed by ProblemBinding. * We try to get all information we can get from this binding. diff --git a/src/test/java/spoon/reflect/visitor/CtScannerTest.java b/src/test/java/spoon/reflect/visitor/CtScannerTest.java index cd06af3fc42..b5188c82c92 100644 --- a/src/test/java/spoon/reflect/visitor/CtScannerTest.java +++ b/src/test/java/spoon/reflect/visitor/CtScannerTest.java @@ -271,9 +271,9 @@ public void exit(CtElement o) { // this is a coarse-grain check to see if the scanner changes // no more exec ref in paramref // also takes into account the comments - assertEquals(3631, counter.nElement + countOfCommentsInCompilationUnits); - assertEquals(2423, counter.nEnter + countOfCommentsInCompilationUnits); - assertEquals(2423, counter.nExit + countOfCommentsInCompilationUnits); + assertEquals(3667, counter.nElement + countOfCommentsInCompilationUnits); + assertEquals(2449, counter.nEnter + countOfCommentsInCompilationUnits); + assertEquals(2449, counter.nExit + countOfCommentsInCompilationUnits); // contract: all AST nodes which are part of Collection or Map are visited first by method "scan(Collection|Map)" and then by method "scan(CtElement)" Counter counter2 = new Counter(); diff --git a/src/test/java/spoon/test/imports/ImportTest.java b/src/test/java/spoon/test/imports/ImportTest.java index 3bff34ace0b..a137f424db3 100644 --- a/src/test/java/spoon/test/imports/ImportTest.java +++ b/src/test/java/spoon/test/imports/ImportTest.java @@ -1529,10 +1529,10 @@ public void testMethodChainAutoImports() { List statements = ctor.getBody().getStatements(); assertEquals("super(context, attributeSet)", statements.get(0).toString()); - assertEquals("mButton = ((Button) (findViewById(page_button_button)))", statements.get(1).toString()); - assertEquals("mCurrentActiveColor = getColor(c4_active_button_color)", statements.get(2).toString()); - assertEquals("mCurrentActiveColor = getResources().getColor(c4_active_button_color)", statements.get(3).toString()); - assertEquals("mCurrentActiveColor = getData().getResources().getColor(c4_active_button_color)", statements.get(4).toString()); + assertEquals("mButton = ((Button) (findViewById(id.page_button_button)))", statements.get(1).toString()); + assertEquals("mCurrentActiveColor = getColor(color.c4_active_button_color)", statements.get(2).toString()); + assertEquals("mCurrentActiveColor = getResources().getColor(color.c4_active_button_color)", statements.get(3).toString()); + assertEquals("mCurrentActiveColor = getData().getResources().getColor(color.c4_active_button_color)", statements.get(4).toString()); } @Test