Merge branch 'pr-5341'

This commit is contained in:
Juan Martín Sotuyo Dodero 2024-11-15 16:15:19 -03:00
commit 59864a7c6c
5 changed files with 129 additions and 10 deletions

View File

@ -25,6 +25,8 @@ This is a {{ site.pmd.release_type }} release.
* [#5293](https://github.com/pmd/pmd/issues/5293): \[java] Deadlock when executing PMD in multiple threads * [#5293](https://github.com/pmd/pmd/issues/5293): \[java] Deadlock when executing PMD in multiple threads
* [#5324](https://github.com/pmd/pmd/issues/5324): \[java] Issue with type inference of nested lambdas * [#5324](https://github.com/pmd/pmd/issues/5324): \[java] Issue with type inference of nested lambdas
* [#5329](https://github.com/pmd/pmd/issues/5329): \[java] Type inference issue with unknown method ref in call chain * [#5329](https://github.com/pmd/pmd/issues/5329): \[java] Type inference issue with unknown method ref in call chain
* java-bestpractices
* [#5083](https://github.com/pmd/pmd/issues/5083): \[java] UnusedPrivateMethod false positive when method reference has no target type
* java-performance * java-performance
* [#5314](https://github.com/pmd/pmd/issues/5314): \[java] InsufficientStringBufferDeclarationRule: Lack of handling for char type parameters * [#5314](https://github.com/pmd/pmd/issues/5314): \[java] InsufficientStringBufferDeclarationRule: Lack of handling for char type parameters

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.FunctionalExprMirror;
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.InvocationMirror; 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.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.MethodRefMirror;
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.PolyExprMirror; import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.PolyExprMirror;
import net.sourceforge.pmd.lang.java.types.internal.infer.InferenceVar.BoundKind; import net.sourceforge.pmd.lang.java.types.internal.infer.InferenceVar.BoundKind;
@ -145,13 +144,19 @@ public final class Infer {
LOG.logResolutionFail(rfe.getFailure()); LOG.logResolutionFail(rfe.getFailure());
// here we set expected if not null, the lambda will have the target type // here we set expected if not null, the lambda will have the target type
expr.setInferredType(expected == null ? ts.UNKNOWN : expected); expr.setInferredType(expected == null ? ts.UNKNOWN : expected);
expr.setFunctionalMethod(ts.UNRESOLVED_METHOD);
if (expr instanceof MethodRefMirror) { if (expr instanceof MethodRefMirror) {
MethodRefMirror mref = (MethodRefMirror) expr; 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); 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 package net.sourceforge.pmd.lang.java.types.internal.infer
import io.kotest.matchers.shouldBe 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.component6
import net.sourceforge.pmd.lang.test.ast.component7 import net.sourceforge.pmd.lang.test.ast.component7
import net.sourceforge.pmd.lang.test.ast.shouldBe import net.sourceforge.pmd.lang.test.ast.shouldBe
import net.sourceforge.pmd.lang.test.ast.shouldMatchN 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.*
import java.util.function.* import java.util.function.*
import java.util.stream.Collector import java.util.stream.Collector
@ -331,12 +331,13 @@ class MethodRefInferenceTest : ProcessorTestSpec({
val t_Archive = acu.firstTypeSignature() val t_Archive = acu.firstTypeSignature()
val mref = acu.descendants(ASTMethodReference::class.java).firstOrThrow() val mref = acu.descendants(ASTMethodReference::class.java).firstOrThrow()
val (getName) = acu.declaredMethodSignatures().toList()
val call = acu.firstMethodCall() val call = acu.firstMethodCall()
spy.shouldHaveMissingCtDecl(call) spy.shouldHaveMissingCtDecl(call)
acu.withTypeDsl { acu.withTypeDsl {
mref.referencedMethod shouldBe ts.UNRESOLVED_METHOD mref.referencedMethod shouldBe getName
mref shouldHaveType ts.UNKNOWN mref shouldHaveType ts.UNKNOWN
call.methodType shouldBe ts.UNRESOLVED_METHOD call.methodType shouldBe ts.UNRESOLVED_METHOD
call.overloadSelectionInfo.apply { call.overloadSelectionInfo.apply {
@ -1093,4 +1094,38 @@ class Scratch {
mref shouldHaveType t_Additioner 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 (mref) = acu.descendants(ASTMethodReference::class.java).toList()
val (lambdaCall, mrefCall) = acu.descendants(ASTMethodCall::class.java).toList() val (lambdaCall, mrefCall) = acu.descendants(ASTMethodCall::class.java).toList()
val (fooDecl) = acu.declaredMethodSignatures().toList()
spy.shouldHaveNoApplicableMethods(lambdaCall) spy.shouldHaveNoApplicableMethods(lambdaCall)
spy.shouldHaveNoApplicableMethods(mrefCall) spy.shouldHaveNoApplicableMethods(mrefCall)
@ -540,7 +541,7 @@ class C {
mref shouldHaveType ts.UNKNOWN mref shouldHaveType ts.UNKNOWN
mref.functionalMethod shouldBe ts.UNRESOLVED_METHOD 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 (lambda) = acu.descendants(ASTLambdaExpression::class.java).toList()
val (mref) = acu.descendants(ASTMethodReference::class.java).toList() val (mref) = acu.descendants(ASTMethodReference::class.java).toList()
val (fooDecl) = acu.declaredMethodSignatures().toList()
spy.shouldHaveNoLambdaCtx(lambda) spy.shouldHaveNoLambdaCtx(lambda)
spy.shouldHaveNoLambdaCtx(mref) spy.shouldHaveNoLambdaCtx(mref)
@ -572,7 +574,7 @@ class C {
mref shouldHaveType ts.UNKNOWN mref shouldHaveType ts.UNKNOWN
mref.functionalMethod shouldBe ts.UNRESOLVED_METHOD 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,80 @@ public class ObtainViaTest {
} }
]]></code> ]]></code>
</test-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-code>
<description>UnusedPrivateMethod #5083 - method reference without target type (3)</description>
<expected-problems>2</expected-problems>
<code><![CDATA[
import java.util.Set;
public class Foo {
private void foo(AnotherUnresolvedType baz) {}
private void foo(UnresolvedType baz) {}
public void bar(Set<UnresolvedType> s) {
s.forEach(this::foo); // Not being able to resolve UnresolvedType and AnotherUnresolvedType means we can't tell which foo is being used here…
}
}
]]></code>
</test-code>
</test-data> </test-data>