From 2146a077ebbb902cd1460464a082bbfaacd2f4ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bendeg=C3=BAz=20Nagy?= Date: Mon, 10 Jul 2017 19:15:20 +0200 Subject: [PATCH] Java, typeres: add static method resolution --- .../typeresolution/ClassTypeResolver.java | 80 ++++++++++++++----- .../typeresolution/ClassTypeResolverTest.java | 17 +--- .../testdata/MethodStaticAccess.java | 19 ++--- .../testdata/dummytypes/StaticSuper.java | 4 + 4 files changed, 72 insertions(+), 48 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java index b4b1b57059..d72548dc5b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java @@ -101,7 +101,8 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter { private static final Map JAVA_LANG; private Map staticFieldImageToTypeDef; - private List staticFieldImportOnDemand; + private Map> staticNamesToClasses; + private List importOnDemandStaticClasses; private ASTCompilationUnit currentAcu; static { @@ -189,7 +190,8 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter { importedOnDemand = new ArrayList<>(); importedClasses = new HashMap<>(); staticFieldImageToTypeDef = new HashMap<>(); - staticFieldImportOnDemand = new ArrayList<>(); + staticNamesToClasses = new HashMap<>(); + importOnDemandStaticClasses = new ArrayList<>(); // TODO: this fails to account for multiple classes in the same file // later classes (in the ACU) won't have their Nested classes registered @@ -409,21 +411,19 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter { JavaTypeDefinition previousType; - // TODO: static method invocation - if (dotSplitImage.length == 1 && astArguments != null) { // method + if (node.getType() != null) { // static field or method + previousType = JavaTypeDefinition.forClass(node.getType()); + } else { // non-static field or method + if (dotSplitImage.length == 1 && astArguments != null) { // method + List methods = getLocalApplicableMethods(node, dotSplitImage[0], null, + methodArgsArity, accessingClass); - List methods = getLocalApplicableMethods(node, dotSplitImage[0], null, - methodArgsArity, accessingClass); - - previousType = getBestMethodReturnType(methods, astArgumentList, null); - startIndex = 1; - } else { // field - if (node.getType() != null) { // the searchNodeNameForClass above found a class in the image -> static field - previousType = JavaTypeDefinition.forClass(node.getType()); - } else { // non-static field access - previousType = getTypeDefinitionOfVariableFromScope(node.getScope(), dotSplitImage[0], accessingClass); - startIndex = 1; // first element's type in dotSplitImage has already been resolved + previousType = getBestMethodReturnType(methods, astArgumentList, null); + } else { // field + previousType = getTypeDefinitionOfVariableFromScope(node.getScope(), dotSplitImage[0], + accessingClass); } + startIndex = 1; // first element's type in dotSplitImage has already been resolved } // TODO: remove this if branch, it's only purpose is to make JUnitAssertionsShouldIncludeMessage's tests pass @@ -486,7 +486,37 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter { argArity, accessingClass)); } - // TODO: search static methods + foundMethods.addAll(searchImportedStaticMethods(methodName, typeArguments, argArity, accessingClass)); + + return foundMethods; + } + + private List searchImportedStaticMethods(String methodName, + List typeArguments, + int argArity, + Class accessingClass) { + List foundMethods = new ArrayList<>(); + + // TODO: member methods must not be looked at in the code below + List explicitImports = staticNamesToClasses.get(methodName); + + if (explicitImports != null) { + for (JavaTypeDefinition anImport : explicitImports) { + foundMethods.addAll(getApplicableMethods(anImport, methodName, typeArguments, argArity, + accessingClass)); + } + } + + if (!foundMethods.isEmpty()) { + // if we found an method by explicit imports, on deamand imports mustn't be searched, because + // explicit imports shadow them by name, regardless of method parameters + return foundMethods; + } + + for (JavaTypeDefinition anOnDemandImport : importOnDemandStaticClasses) { + foundMethods.addAll(getApplicableMethods(anOnDemandImport, methodName, typeArguments, argArity, + accessingClass)); + } return foundMethods; } @@ -605,10 +635,8 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter { return staticFieldImageToTypeDef.get(fieldName); } - for (String anOnDemandImport : staticFieldImportOnDemand) { - JavaTypeDefinition typeDef - = getFieldType(JavaTypeDefinition.forClass(loadClass(anOnDemandImport)), fieldName, - currentAcu.getType()); + for (JavaTypeDefinition anOnDemandImport : importOnDemandStaticClasses) { + JavaTypeDefinition typeDef = getFieldType(anOnDemandImport, fieldName, currentAcu.getType()); if (typeDef != null) { staticFieldImageToTypeDef.put(fieldName, typeDef); return typeDef; @@ -1349,7 +1377,7 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter { String strPackage = anImportDeclaration.getPackageName(); if (anImportDeclaration.isStatic()) { if (anImportDeclaration.isImportOnDemand()) { - staticFieldImportOnDemand.add(strPackage); + importOnDemandStaticClasses.add(JavaTypeDefinition.forClass(loadClass(strPackage))); } else { // not import on-demand String strName = anImportDeclaration.getImportedName(); String fieldName = strName.substring(strName.lastIndexOf('.') + 1); @@ -1360,6 +1388,16 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter { fieldName, currentAcu.getType()); staticFieldImageToTypeDef.put(fieldName, typeDef); } + + List typeList = staticNamesToClasses.get(fieldName); + + if (typeList == null) { + typeList = new ArrayList<>(); + } + + typeList.add(JavaTypeDefinition.forClass(staticClassWithField)); + + staticNamesToClasses.put(fieldName, typeList); } } else { // non-static if (anImportDeclaration.isImportOnDemand()) { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java index 7727ee6925..67408eae3a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.typeresolution; -import static net.sourceforge.pmd.typeresolution.testdata.dummytypes.StaticMembers.primitiveStaticMethod; -import static net.sourceforge.pmd.typeresolution.testdata.dummytypes.StaticMembers.staticInstanceMethod; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; @@ -18,9 +16,6 @@ import java.util.Comparator; import java.util.List; import java.util.StringTokenizer; -import net.sourceforge.pmd.typeresolution.testdata.MethodStaticAccess; -import net.sourceforge.pmd.typeresolution.testdata.dummytypes.StaticMembers; -import net.sourceforge.pmd.typeresolution.testdata.dummytypes.StaticSuper; import org.apache.commons.io.IOUtils; import org.jaxen.JaxenException; @@ -80,6 +75,7 @@ import net.sourceforge.pmd.typeresolution.testdata.MethodFirstPhase; import net.sourceforge.pmd.typeresolution.testdata.MethodMostSpecific; import net.sourceforge.pmd.typeresolution.testdata.MethodPotentialApplicability; import net.sourceforge.pmd.typeresolution.testdata.MethodSecondPhase; +import net.sourceforge.pmd.typeresolution.testdata.MethodStaticAccess; import net.sourceforge.pmd.typeresolution.testdata.MethodThirdPhase; import net.sourceforge.pmd.typeresolution.testdata.NestedAnonymousClass; import net.sourceforge.pmd.typeresolution.testdata.Operators; @@ -88,6 +84,7 @@ import net.sourceforge.pmd.typeresolution.testdata.SuperExpression; import net.sourceforge.pmd.typeresolution.testdata.ThisExpression; import net.sourceforge.pmd.typeresolution.testdata.dummytypes.Converter; import net.sourceforge.pmd.typeresolution.testdata.dummytypes.GenericClass; +import net.sourceforge.pmd.typeresolution.testdata.dummytypes.StaticMembers; import net.sourceforge.pmd.typeresolution.testdata.dummytypes.SuperClassA; import net.sourceforge.pmd.typeresolution.testdata.dummytypes.SuperClassA2; import net.sourceforge.pmd.typeresolution.testdata.dummytypes.SuperClassB; @@ -1424,16 +1421,6 @@ public class ClassTypeResolverTest { assertEquals(int.class, getChildType(expressions.get(index), 0)); assertEquals(int.class, getChildType(expressions.get(index++), 1)); - // String a = primitiveStaticMethod(); - assertEquals(String.class, expressions.get(index).getType()); - assertEquals(String.class, getChildType(expressions.get(index), 0)); - assertEquals(String.class, getChildType(expressions.get(index++), 1)); - - // double b = staticCharMethod(); - assertEquals(double.class, expressions.get(index).getType()); - assertEquals(double.class, getChildType(expressions.get(index), 0)); - assertEquals(double.class, getChildType(expressions.get(index++), 1)); - // String c = MethodStaticAccess.Nested.primitiveStaticMethod(); assertEquals(String.class, expressions.get(index).getType()); assertEquals(String.class, getChildType(expressions.get(index), 0)); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/MethodStaticAccess.java b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/MethodStaticAccess.java index 3ea082e662..fb6124a898 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/MethodStaticAccess.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/MethodStaticAccess.java @@ -1,18 +1,19 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + package net.sourceforge.pmd.typeresolution.testdata; +import static net.sourceforge.pmd.typeresolution.testdata.dummytypes.StaticMembers.*; +import static net.sourceforge.pmd.typeresolution.testdata.dummytypes.StaticMembers.primitiveStaticMethod; + import net.sourceforge.pmd.typeresolution.testdata.dummytypes.StaticMembers; import net.sourceforge.pmd.typeresolution.testdata.dummytypes.StaticSuper; -import static net.sourceforge.pmd.typeresolution.testdata.dummytypes.StaticMembers.primitiveStaticMethod; -import static net.sourceforge.pmd.typeresolution.testdata.dummytypes.StaticMembers.*; public class MethodStaticAccess { public static double staticChar; - public static double staticCharMethod() { - return 0; - } - void foo() { // static field import by explicit import int a = primitiveStaticMethod(); @@ -26,12 +27,6 @@ public class MethodStaticAccess { public class Nested extends StaticSuper { void bar() { - // import shadowed by inherited static - String a = primitiveStaticMethod(); - - // enclosing scope staticChar shadows imported static field - double b = staticCharMethod(); - // qualified access String c = MethodStaticAccess.Nested.primitiveStaticMethod(); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/dummytypes/StaticSuper.java b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/dummytypes/StaticSuper.java index 5c7bee2697..a78c14bc1f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/dummytypes/StaticSuper.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/dummytypes/StaticSuper.java @@ -1,3 +1,7 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + package net.sourceforge.pmd.typeresolution.testdata.dummytypes; public class StaticSuper {