diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodType.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodType.java index ff5bd703fd..8423b2cf5e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodType.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodType.java @@ -53,4 +53,12 @@ public class MethodType { public boolean isAbstract() { return Modifier.isAbstract(method.getModifiers()); } + + public JavaTypeDefinition getArgTypeIncludingVararg(int index) { + if (index < argTypes.size() - 1) { + return argTypes.get(index); + } else { + return getVarargComponentType(); + } + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodTypeResolution.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodTypeResolution.java index 1168422692..b8fd7fd2c9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodTypeResolution.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodTypeResolution.java @@ -48,15 +48,37 @@ public final class MethodTypeResolution { } public static boolean checkSubtypeability(MethodType method, MethodType subtypeableMethod) { - List subtypeableArgs = subtypeableMethod.getParameterTypes(); - List methodTypeArgs = method.getParameterTypes(); + List subtypeableParams = subtypeableMethod.getParameterTypes(); + List methodParams = method.getParameterTypes(); - // TODO: add support for not matching arity methods - for (int index = 0; index < subtypeableArgs.size(); ++index) { - if (!isSubtypeable(methodTypeArgs.get(index), subtypeableArgs.get(index))) { - return false; + if (!method.getMethod().isVarArgs() && !subtypeableMethod.getMethod().isVarArgs()) { + for (int index = 0; index < subtypeableParams.size(); ++index) { + if (!isSubtypeable(methodParams.get(index), subtypeableParams.get(index))) { + return false; + } } + } else if (method.getMethod().isVarArgs() && subtypeableMethod.getMethod().isVarArgs()) { + + if (methodParams.size() < subtypeableParams.size()) { + for (int index = 0; index < subtypeableParams.size(); ++index) { + if (!isSubtypeable(method.getArgTypeIncludingVararg(index), + subtypeableMethod.getArgTypeIncludingVararg(index))) { + return false; + } + } + } else { + for (int index = 0; index < methodParams.size(); ++index) { + if (!isSubtypeable(method.getArgTypeIncludingVararg(index), + subtypeableMethod.getArgTypeIncludingVararg(index))) { + return false; + } + } + } + + } else { + throw new IllegalStateException("These methods can only be vararg at the same time:\n" + + method.toString() + "\n" + subtypeableMethod.toString()); } return true; @@ -202,10 +224,7 @@ public final class MethodTypeResolution { */ public static JavaTypeDefinition getBestMethodReturnType(List methods, ASTArgumentList arguments, List typeArgs) { - if (methods.size() == 1) { - return methods.get(0).getReturnType(); // TODO: remove this in the end, needed to pass some previous tests - } - + List selectedMethods = selectMethodsFirstPhase(methods, arguments, typeArgs); if (!selectedMethods.isEmpty()) { return selectMostSpecificMethod(selectedMethods).getReturnType(); @@ -218,10 +237,7 @@ public final class MethodTypeResolution { selectedMethods = selectMethodsThirdPhase(methods, arguments, typeArgs); if (!selectedMethods.isEmpty()) { - if (selectedMethods.size() == 1) { - return selectedMethods.get(0).getReturnType(); - // TODO: add selecting most specific vararg method - } + return selectMostSpecificMethod(selectedMethods).getReturnType(); } return null; 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 675791ba24..5a493f9e1f 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 @@ -1325,6 +1325,10 @@ public class ClassTypeResolverTest { assertEquals(int.class, expressions.get(index).getType()); assertEquals(int.class, getChildType(expressions.get(index), 0)); assertEquals(int.class, getChildType(expressions.get(index++), 1)); + // String d = mostSpecific(10, 10, 10); + assertEquals(String.class, expressions.get(index).getType()); + assertEquals(String.class, getChildType(expressions.get(index), 0)); + assertEquals(String.class, getChildType(expressions.get(index++), 1)); // Make sure we got them all assertEquals("All expressions not tested", index, expressions.size()); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/MethodThirdPhase.java b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/MethodThirdPhase.java index 290ed7cb03..e7740157d1 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/MethodThirdPhase.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/MethodThirdPhase.java @@ -15,8 +15,16 @@ public class MethodThirdPhase { // component type determined properly int c = vararg(10, "", "", ""); - // TODO: add most specific tests among vararg conversion + // most specific vararg method + String d = mostSpecific(10, 10, 10); + } + Exception mostSpecific(Number... b) { + return null; + } + + String mostSpecific(Integer... b) { + return null; } Exception vararg(int a, Number... b) { diff --git a/src/site/markdown/overview/changelog.md b/src/site/markdown/overview/changelog.md index a8232684de..c8280741ed 100644 --- a/src/site/markdown/overview/changelog.md +++ b/src/site/markdown/overview/changelog.md @@ -34,7 +34,7 @@ CPD will therefore have less false positives and false negatives. As part of Google Summer of Code 2017, [Bendegúz Nagy](https://github.com/WinterGrascph)'s work on type resolution for Java continues. For this release he has extended support for method calls. -Method shadowing and overloading are supported, as is varargs management. However, the selection of the target method upon the presence +Method shadowing and overloading are supported, as are varargs. However, the selection of the target method upon the presence of generics and type inference is still work in progress. Expect it in forecoming releases. #### Metrics Framework @@ -83,4 +83,5 @@ Based on those metrics, rules like "GodClass" detection can be implemented more * [#486](https://github.com/pmd/pmd/pull/486): \[java] Add basic method typeresolution - [Bendegúz Nagy](https://github.com/WinterGrascph) * [#492](https://github.com/pmd/pmd/pull/492): \[java] Typeresolution for overloaded methods - [Bendegúz Nagy](https://github.com/WinterGrascph) * [#495](https://github.com/pmd/pmd/pull/495): \[core] Custom rule reinitialization code - [Clément Fournier](https://github.com/oowekyala) +* [#501](https://github.com/pmd/pmd/pull/501): \[java] Add support for most specific vararg method type resolution - [Bendegúz Nagy](https://github.com/WinterGrascph)