diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/Infer.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/Infer.java index 7982304210..e51ef905f2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/Infer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/Infer.java @@ -36,7 +36,6 @@ import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.CtorInvocat import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.FunctionalExprMirror; import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.InvocationMirror; import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.InvocationMirror.MethodCtDecl; -import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.LambdaExprMirror; import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.MethodRefMirror; import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.PolyExprMirror; import net.sourceforge.pmd.lang.java.types.internal.infer.InferenceVar.BoundKind; @@ -145,13 +144,19 @@ public final class Infer { LOG.logResolutionFail(rfe.getFailure()); // here we set expected if not null, the lambda will have the target type expr.setInferredType(expected == null ? ts.UNKNOWN : expected); + expr.setFunctionalMethod(ts.UNRESOLVED_METHOD); if (expr instanceof MethodRefMirror) { MethodRefMirror mref = (MethodRefMirror) expr; - mref.setFunctionalMethod(ts.UNRESOLVED_METHOD); + if (!TypeOps.isUnresolved(mref.getTypeToSearch())) { + JMethodSig exactMethod = ExprOps.getExactMethod(mref); + if (exactMethod != null) { + // as a fallback, if the method reference is exact, + // we populate the compile time decl anyway. + mref.setCompileTimeDecl(exactMethod); + return; + } + } mref.setCompileTimeDecl(ts.UNRESOLVED_METHOD); - } else { - LambdaExprMirror lambda = (LambdaExprMirror) expr; - lambda.setFunctionalMethod(ts.UNRESOLVED_METHOD); } } } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/MethodRefInferenceTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/MethodRefInferenceTest.kt index e928dda72b..ef10cf77df 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/MethodRefInferenceTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/MethodRefInferenceTest.kt @@ -5,12 +5,12 @@ package net.sourceforge.pmd.lang.java.types.internal.infer import io.kotest.matchers.shouldBe +import net.sourceforge.pmd.lang.java.ast.* +import net.sourceforge.pmd.lang.java.types.* import net.sourceforge.pmd.lang.test.ast.component6 import net.sourceforge.pmd.lang.test.ast.component7 import net.sourceforge.pmd.lang.test.ast.shouldBe import net.sourceforge.pmd.lang.test.ast.shouldMatchN -import net.sourceforge.pmd.lang.java.ast.* -import net.sourceforge.pmd.lang.java.types.* import java.util.* import java.util.function.* import java.util.stream.Collector @@ -331,12 +331,13 @@ class MethodRefInferenceTest : ProcessorTestSpec({ val t_Archive = acu.firstTypeSignature() val mref = acu.descendants(ASTMethodReference::class.java).firstOrThrow() + val (getName) = acu.declaredMethodSignatures().toList() val call = acu.firstMethodCall() spy.shouldHaveMissingCtDecl(call) acu.withTypeDsl { - mref.referencedMethod shouldBe ts.UNRESOLVED_METHOD + mref.referencedMethod shouldBe getName mref shouldHaveType ts.UNKNOWN call.methodType shouldBe ts.UNRESOLVED_METHOD call.overloadSelectionInfo.apply { @@ -1093,4 +1094,38 @@ class Scratch { mref shouldHaveType t_Additioner } } + + parserTest("Method ref without target type still populates CTDecl if exact") { + val (acu, spy) = parser.parseWithTypeInferenceSpy( + """ + class GenerationType { + static { + foo(GenerationType::isAndroidType); + } + { + foo(this::instance); + } + + private boolean instance(String data) {} + private static boolean isAndroidType(String data) { + return "android".equals(data); + } + } + """.trimIndent() + ) + + val (instance, static) = acu.declaredMethodSignatures() + + val mrefs = acu.descendants(ASTMethodReference::class.java).toList() + val (staticMref, instanceMref) = mrefs + + spy.shouldBeOk { + mrefs.forEach { + it shouldHaveType ts.UNKNOWN + it.functionalMethod shouldBe ts.UNRESOLVED_METHOD + } + instanceMref.referencedMethod shouldBe instance + staticMref.referencedMethod shouldBe static + } + } }) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/UnresolvedTypesRecoveryTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/UnresolvedTypesRecoveryTest.kt index 45c13cf569..7a8b14a2af 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/UnresolvedTypesRecoveryTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/UnresolvedTypesRecoveryTest.kt @@ -530,6 +530,7 @@ class C { val (mref) = acu.descendants(ASTMethodReference::class.java).toList() val (lambdaCall, mrefCall) = acu.descendants(ASTMethodCall::class.java).toList() + val (fooDecl) = acu.declaredMethodSignatures().toList() spy.shouldHaveNoApplicableMethods(lambdaCall) spy.shouldHaveNoApplicableMethods(mrefCall) @@ -540,7 +541,7 @@ class C { mref shouldHaveType ts.UNKNOWN mref.functionalMethod shouldBe ts.UNRESOLVED_METHOD - mref.referencedMethod shouldBe ts.UNRESOLVED_METHOD + mref.referencedMethod shouldBe fooDecl // still populated because unambiguous } } @@ -562,6 +563,7 @@ class C { val (lambda) = acu.descendants(ASTLambdaExpression::class.java).toList() val (mref) = acu.descendants(ASTMethodReference::class.java).toList() + val (fooDecl) = acu.declaredMethodSignatures().toList() spy.shouldHaveNoLambdaCtx(lambda) spy.shouldHaveNoLambdaCtx(mref) @@ -572,7 +574,7 @@ class C { mref shouldHaveType ts.UNKNOWN mref.functionalMethod shouldBe ts.UNRESOLVED_METHOD - mref.referencedMethod shouldBe ts.UNRESOLVED_METHOD + mref.referencedMethod shouldBe fooDecl // still populated because unambiguous } } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml index ae0fc5e242..5f38fdcf2e 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml @@ -2237,5 +2237,64 @@ public class ObtainViaTest { } ]]> + + UnusedPrivateMethod #5083 - method reference without target type + 0 + predicate; + + private static boolean isAppleType(String data) { + return "apple".equals(data); + } + + private static boolean isRokuType(String data) { + return "roku".equals(data); + } + + private static boolean isSamsungType(String data) { + return "samsung".equals(data); + } + + private static boolean isAmazonType(String data) { + return "amazon".equals(data); + } + + private static boolean isAndroidType(String data) { + return "android".equals(data); + } + } + ]]> + + + + UnusedPrivateMethod #5083 - method reference without target type (2) + 0 + +