Fix #5083 - mref without target type but with exact method

has compile time decl
This commit is contained in:
Clément Fournier 2024-11-15 16:28:08 +01:00
parent a72ac5845b
commit b264fa14e1
No known key found for this signature in database
GPG Key ID: 6F9389D8E390BD4C
4 changed files with 111 additions and 10 deletions

View File

@ -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);
}
}
}

View File

@ -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
}
}
})

View File

@ -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
}
}

View File

@ -2237,5 +2237,64 @@ public class ObtainViaTest {
}
]]></code>
</test-code>
<test-code>
<description>UnusedPrivateMethod #5083 - method reference without target type</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
@Getter
@RequiredArgsConstructor
enum GenerationType {
APPLE_DESKTOP("https://apps.apple.com/app/id", GenerationType::isAppleType),
APPLE_ITUNES("https://itunes.apple.com/app/id", GenerationType::isAppleType),
SAMSUNG("https://www.samsung.com/us/appstore/app/", GenerationType::isSamsungType),
ROKU("https://channelstore.roku.com/details/", GenerationType::isRokuType),
AMAZON("https://www.amazon.com/dp/", GenerationType::isAmazonType),
ANDROID("https://play.google.com/store/apps/details?id=", GenerationType::isAndroidType);
private final String baseUrl;
private final Predicate<String> 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);
}
}
]]></code>
</test-code>
<test-code>
<description>UnusedPrivateMethod #5083 - method reference without target type (2)</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
class GenerationType {
static {
foo(GenerationType::isAndroidType);
}
{
foo(this::instance);
}
private boolean instance(String data) {}
private static boolean isAndroidType(String data) {
return "android".equals(data);
}
}
]]></code>
</test-code>
</test-data>