forked from phoedos/pmd
Merge branch 'pr-5341'
This commit is contained in:
commit
59864a7c6c
@ -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
|
||||
* [#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
|
||||
* java-bestpractices
|
||||
* [#5083](https://github.com/pmd/pmd/issues/5083): \[java] UnusedPrivateMethod false positive when method reference has no target type
|
||||
* java-performance
|
||||
* [#5314](https://github.com/pmd/pmd/issues/5314): \[java] InsufficientStringBufferDeclarationRule: Lack of handling for char type parameters
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2237,5 +2237,80 @@ 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-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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user