diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java index 1398cdb9fa8..fd02601f871 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java @@ -207,6 +207,7 @@ import spoon.reflect.reference.CtLocalVariableReference; import spoon.reflect.reference.CtPackageReference; import spoon.reflect.reference.CtParameterReference; +import spoon.reflect.reference.CtReference; import spoon.reflect.reference.CtTypeParameterReference; import spoon.reflect.reference.CtTypeReference; import spoon.reflect.reference.CtVariableReference; @@ -281,6 +282,8 @@ public class BuilderContext { boolean isLambdaParameterImplicitlyTyped = true; + boolean ignoreComputeImports = false; + /** * Stack of all parents elements */ @@ -412,6 +415,57 @@ public CtTypeReference getBoundedTypeReference(TypeBinding binding) { return ref; } + /** + * Try to get the declaring reference (package or type) from imports of the current + * compilation unit declaration (current class). This method returns a CtReference + * which can be a CtTypeReference if it retrieves the information in an static import, + * a CtPackageReference if it retrieves the information in an standard import, otherwise + * it returns null. + * + * @param expectedName + * Name expected in imports. + * @return CtReference which can be a CtTypeReference, a CtPackageReference or null. + */ + public CtReference getDeclaringReferenceFromImports(char[] expectedName) { + if (context.compilationunitdeclaration != null && context.compilationunitdeclaration.imports != null) { + for (ImportReference anImport : context.compilationunitdeclaration.imports) { + if (CharOperation.equals(anImport.getImportName()[anImport.getImportName().length - 1], expectedName)) { + if (anImport.isStatic()) { + int indexDeclaring = 2; + if ((anImport.bits & ASTNode.OnDemand) != 0) { + // With .* + indexDeclaring = 1; + } + char[][] packageName = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - indexDeclaring); + char[][] className = CharOperation.subarray(anImport.getImportName(), anImport.getImportName().length - indexDeclaring, anImport.getImportName().length - (indexDeclaring - 1)); + final PackageBinding aPackage = context.compilationunitdeclaration.scope.environment.createPackage(packageName); + final MissingTypeBinding declaringType = context.compilationunitdeclaration.scope.environment.createMissingType(aPackage, className); + context.ignoreComputeImports = true; + final CtTypeReference typeReference = getTypeReference(declaringType); + context.ignoreComputeImports = false; + return typeReference; + } else { + char[][] chars = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - 1); + Binding someBinding = context.compilationunitdeclaration.scope.findImport(chars, false, false); + PackageBinding packageBinding; + if (someBinding != null && someBinding.isValidBinding() && someBinding instanceof PackageBinding) { + packageBinding = (PackageBinding) someBinding; + } else { + packageBinding = context.compilationunitdeclaration.scope.environment.createPackage(chars); + if (packageBinding == null) { + // Big crisis here. We are already in noclasspath mode but JDT doesn't support always + // creation of a package in this mode. So, if we are in this brace, we make the job of JDT... + packageBinding = new PackageBinding(chars, null, context.compilationunitdeclaration.scope.environment); + } + } + return getPackageReference(packageBinding); + } + } + } + } + return null; + } + @SuppressWarnings("unchecked") public CtExecutableReference getExecutableReference(MethodBinding exec) { if (exec == null) { @@ -423,18 +477,9 @@ public CtExecutableReference getExecutableReference(MethodBinding exec) { ref.setType((CtTypeReference) getTypeReference(exec.returnType)); if (exec instanceof ProblemMethodBinding) { - // We try to check in imports if there is the correct package of the type. - if (context.compilationunitdeclaration != null && context.compilationunitdeclaration.imports != null) { - for (ImportReference anImport : context.compilationunitdeclaration.imports) { - if (CharOperation.equals(anImport.getImportName()[anImport.getImportName().length - 1], exec.constantPoolName())) { - char[][] packageName = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - 2); - char[][] className = CharOperation.subarray(anImport.getImportName(), anImport.getImportName().length - 2, anImport.getImportName().length - 1); - final PackageBinding aPackage = context.compilationunitdeclaration.scope.environment.createPackage(packageName); - final MissingTypeBinding declaringType = context.compilationunitdeclaration.scope.environment.createMissingType(aPackage, className); - ref.setDeclaringType(getTypeReference(declaringType)); - break; - } - } + final CtReference declaringType = getDeclaringReferenceFromImports(exec.constantPoolName()); + if (declaringType instanceof CtTypeReference) { + ref.setDeclaringType((CtTypeReference) declaringType); } if (exec.isConstructor()) { // super() invocation have a good declaring class. @@ -535,27 +580,12 @@ public CtTypeReference getTypeReference(TypeBinding binding) { ref = factory.Core().createTypeReference(); ref.setSimpleName(new String(binding.sourceName())); ref.setPackage(getPackageReference(binding.getPackage())); - // We try to check in imports if there is the correct package of the type. - if (context.compilationunitdeclaration != null && context.compilationunitdeclaration.imports != null) { - for (ImportReference anImport : context.compilationunitdeclaration.imports) { - if (CharOperation.equals(anImport.getImportName()[anImport.getImportName().length - 1], binding.sourceName())) { - char[][] chars = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - 1); - Binding someBinding = context.compilationunitdeclaration.scope.findImport(chars, false, false); - PackageBinding packageBinding; - if (someBinding != null && someBinding.isValidBinding() && someBinding instanceof PackageBinding) { - packageBinding = (PackageBinding) someBinding; - } else { - packageBinding = context.compilationunitdeclaration.scope.environment.createPackage(chars); - if (packageBinding == null) { - // Big crisis here. We are already in noclasspath mode since the check `binding instance MissingTypeBinding` - // but JDT doesn't support always creation of a package in this mode. So, if we are in this brace, we make - // the job of JDT... - packageBinding = new PackageBinding(chars, null, context.compilationunitdeclaration.scope.environment); - } - } - ref.setPackage(getPackageReference(packageBinding)); - break; - } + if (!context.ignoreComputeImports) { + final CtReference declaring = references.getDeclaringReferenceFromImports(binding.sourceName()); + if (declaring instanceof CtPackageReference) { + ref.setPackage((CtPackageReference) declaring); + } else if (declaring instanceof CtTypeReference) { + ref.setDeclaringType((CtTypeReference) declaring); } } } else if (binding instanceof BinaryTypeBinding) { @@ -2392,25 +2422,20 @@ public boolean visit(MessageSend messageSend, BlockScope scope) { if (messageSend.receiver.resolvedType == null) { // It is crisis dude! static context, we don't have much more information. if (messageSend.receiver instanceof SingleNameReference) { - final CtTypeReference typeReference = factory.Core().createTypeReference(); + CtTypeReference typeReference = factory.Core().createTypeReference(); typeReference.setSimpleName(messageSend.receiver.toString()); - if (context.compilationunitdeclaration != null && context.compilationunitdeclaration.imports != null) { - for (ImportReference anImport : context.compilationunitdeclaration.imports) { - if (CharOperation.equals(anImport.getImportName()[anImport.getImportName().length - 1], ((SingleNameReference) messageSend.receiver).token)) { - char[][] packageName = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - 1); - CtPackageReference packageRef = factory.Core().createPackageReference(); - packageRef.setSimpleName(CharOperation.toString(packageName)); - typeReference.setPackage(packageRef); - break; - } - } + final CtReference declaring = references.getDeclaringReferenceFromImports(((SingleNameReference) messageSend.receiver).token); + if (declaring instanceof CtPackageReference) { + typeReference.setPackage((CtPackageReference) declaring); + } else if (declaring instanceof CtTypeReference) { + typeReference = (CtTypeReference) declaring; } ref.setDeclaringType(typeReference); } else if (messageSend.receiver instanceof QualifiedNameReference) { QualifiedNameReference qualifiedNameReference = (QualifiedNameReference) messageSend.receiver; - char[][] packageName = CharOperation.subarray(qualifiedNameReference.tokens, 0, qualifiedNameReference.tokens.length - 2); - char[][] className = CharOperation.subarray(qualifiedNameReference.tokens, qualifiedNameReference.tokens.length - 2, qualifiedNameReference.tokens.length - 1); + char[][] packageName = CharOperation.subarray(qualifiedNameReference.tokens, 0, qualifiedNameReference.tokens.length - 1); + char[][] className = CharOperation.subarray(qualifiedNameReference.tokens, qualifiedNameReference.tokens.length - 1, qualifiedNameReference.tokens.length); if (packageName.length > 0) { final PackageBinding aPackage = context.compilationunitdeclaration.scope.environment.createPackage(packageName); final MissingTypeBinding declaringType = context.compilationunitdeclaration.scope.environment.createMissingType(aPackage, className); @@ -2773,8 +2798,15 @@ public boolean visit(QualifiedNameReference qualifiedNameReference, BlockScope s va = factory.Core().createFieldRead(); } va.setVariable(references.getVariableReference((ProblemBinding) qualifiedNameReference.binding)); + // In no classpath mode and with qualified name, the type given by JDT is wrong... + if (va.getVariable() instanceof CtFieldReference) { + final char[][] declaringClass = CharOperation.subarray(qualifiedNameReference.tokens, 0, qualifiedNameReference.tokens.length - 1); + final MissingTypeBinding declaringType = context.compilationunitdeclaration.scope.environment.createMissingType(null, declaringClass); + ((CtFieldReference) va.getVariable()).setDeclaringType(references.getTypeReference(declaringType)); + ((CtFieldReference) va.getVariable()).setStatic(true); + } // In no classpath mode and with qualified name, the binding don't have a good name. - va.getVariable().setSimpleName(createTypeName(qualifiedNameReference.getName())); + va.getVariable().setSimpleName(createTypeName(CharOperation.subarray(qualifiedNameReference.tokens, qualifiedNameReference.tokens.length - 1, qualifiedNameReference.tokens.length))); context.enter(va, qualifiedNameReference); return false; } else { @@ -2851,18 +2883,13 @@ public boolean visit(SingleNameReference singleNameReference, BlockScope scope) } else if (singleNameReference.binding instanceof ProblemBinding) { if (context.stack.peek().element instanceof CtInvocation) { final CtTypeAccess ta = factory.Core().createTypeAccess(); - final CtTypeReference typeReference = factory.Core().createTypeReference(); + CtTypeReference typeReference = factory.Core().createTypeReference(); typeReference.setSimpleName(new String(singleNameReference.binding.readableName())); - if (context.compilationunitdeclaration != null && context.compilationunitdeclaration.imports != null) { - for (ImportReference anImport : context.compilationunitdeclaration.imports) { - if (CharOperation.equals(anImport.getImportName()[anImport.getImportName().length - 1], singleNameReference.token)) { - char[][] packageName = CharOperation.subarray(anImport.getImportName(), 0, anImport.getImportName().length - 1); - CtPackageReference packageRef = factory.Core().createPackageReference(); - packageRef.setSimpleName(CharOperation.toString(packageName)); - typeReference.setPackage(packageRef); - break; - } - } + final CtReference declaring = references.getDeclaringReferenceFromImports(singleNameReference.token); + if (declaring instanceof CtPackageReference) { + typeReference.setPackage((CtPackageReference) declaring); + } else if (declaring instanceof CtTypeReference) { + typeReference = (CtTypeReference) declaring; } ta.setType(typeReference); context.enter(ta, singleNameReference); diff --git a/src/test/java/spoon/test/imports/ImportTest.java b/src/test/java/spoon/test/imports/ImportTest.java index 60ef9029f09..72366bef401 100644 --- a/src/test/java/spoon/test/imports/ImportTest.java +++ b/src/test/java/spoon/test/imports/ImportTest.java @@ -187,13 +187,13 @@ public boolean matches(CtInvocation element) { assertCorrectInvocation(new Expected().name("makeBurritos").target("Tacos.Burritos").declaringType("Burritos").typeIsNull(false), elements.get(7)); // Invocation for a static method in an inner class with the declaring class specified. - assertCorrectInvocation(new Expected().name("staticD").target("C.D").declaringType("C.D").typeIsNull(true), elements.get(8)); + assertCorrectInvocation(new Expected().name("staticD").target("C.D").declaringType("D").typeIsNull(true), elements.get(8)); // Invocation for a static method in an inner class without the declaring class specified. assertCorrectInvocation(new Expected().name("staticD").target("pack2.C.D").declaringType("D").typeIsNull(true), elements.get(9)); // Invocation for a static method in an inner class with the declaring class specified and a return type. - assertCorrectInvocation(new Expected().name("staticD").target("C.D").declaringType("C.D").typeIsNull(false), elements.get(10)); + assertCorrectInvocation(new Expected().name("staticD").target("C.D").declaringType("D").typeIsNull(false), elements.get(10)); // Invocation for a static method in an inner class without the declaring class specified and a return type. assertCorrectInvocation(new Expected().name("staticD").target("pack2.C.D").declaringType("D").typeIsNull(false), elements.get(11)); diff --git a/src/test/java/spoon/test/variable/AccessTest.java b/src/test/java/spoon/test/variable/AccessTest.java index e7f2c7fd5d3..2e1cf2e5b26 100644 --- a/src/test/java/spoon/test/variable/AccessTest.java +++ b/src/test/java/spoon/test/variable/AccessTest.java @@ -192,7 +192,7 @@ public void testVariableAccessInNoClasspath() throws Exception { assertNotNull(element.getVariable()); } - assertEquals("java.lang.Class mclass = ((java.lang.Class)(ModelFacade.USE_CASE))", elements.get(0).getParent().toString()); + assertEquals("java.lang.Class mclass = ((java.lang.Class)(org.argouml.model.ModelFacade.USE_CASE))", elements.get(0).getParent().toString()); assertEquals("new PropPanelButton(this , buttonPanel , _navUpIcon , org.argouml.i18n.Translator.localize(\"UMLMenu\", \"button.go-up\") , \"navigateNamespace\" , null)", elements.get(2).getParent().toString()); } }