From 5931b6601cc14d245c01cedf7f24eed045a6d31a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Nov 2024 15:23:52 +0100 Subject: [PATCH] [java] Fix #5097 - problem with unchecked conversion --- .../pmd/lang/java/types/TypeOps.java | 6 ++++ .../pmd/lang/java/types/SubtypingTest.kt | 9 +++-- .../infer/UnresolvedTypesRecoveryTest.kt | 35 +++++++++++++++++++ .../bestpractices/xml/UnusedPrivateMethod.xml | 32 +++++++++++++++++ 4 files changed, 79 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java index a6ac882819..7f976967d8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java @@ -717,6 +717,12 @@ public final class TypeOps { // no unchecked warning. return allArgsAreUnboundedWildcards(sargs) ? Convertibility.UNCHECKED_NO_WARNING : Convertibility.UNCHECKED_WARNING; + } else if (sargs.isEmpty()) { + // C <: |C| + // JLS 4.10.2 + // unchecked conversion converts a raw type to a generic type + // subtyping converts a generic type to its raw type + return Convertibility.SUBTYPING; } if (targs.size() != sargs.size()) { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/SubtypingTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/SubtypingTest.kt index 03cfcddb4a..d3b3ba18c6 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/SubtypingTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/SubtypingTest.kt @@ -13,14 +13,14 @@ import io.kotest.property.Exhaustive import io.kotest.property.checkAll import io.kotest.property.exhaustive.ints import io.kotest.property.forAll -import net.sourceforge.pmd.lang.test.ast.shouldBeA import net.sourceforge.pmd.lang.java.ast.ParserTestCtx import net.sourceforge.pmd.lang.java.symbols.internal.UnresolvedClassStore import net.sourceforge.pmd.lang.java.symbols.internal.asm.createUnresolvedAsmSymbol -import net.sourceforge.pmd.lang.java.types.TypeConversion.* -import net.sourceforge.pmd.lang.java.types.TypeOps.Convertibility.* +import net.sourceforge.pmd.lang.java.types.TypeConversion.capture +import net.sourceforge.pmd.lang.java.types.TypeOps.Convertibility.UNCHECKED_NO_WARNING import net.sourceforge.pmd.lang.java.types.testdata.ComparableList import net.sourceforge.pmd.lang.java.types.testdata.SomeEnum +import net.sourceforge.pmd.lang.test.ast.shouldBeA import kotlin.test.assertTrue /** @@ -187,6 +187,9 @@ class SubtypingTest : FunSpec({ Class shouldBeUncheckedSubtypeOf `Class{String}` ts.STRING shouldBeSubtypeOf `Comparable{Wildcard}` + + val unresolvedT = ts.createUnresolvedAsmSymbol("foo") + unresolvedT[`?`] shouldBeSubtypeOf ts.rawType(unresolvedT) } test("Test wildcard subtyping") { 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..82fdf359cd 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 @@ -702,4 +702,39 @@ class C { buildItem.methodType.symbol shouldBe buildItemDecl } } + + parserTest("Unresolved type should also allow unchecked conversion") { + // The problem here is that ConstraintViolation is not convertible to ConstraintViolation, + // because ConstraintViolation is not on the classpath. + + val (acu, _) = parser.parseWithTypeInferenceSpy( + """ + import java.util.Set; + class Foo { + private void foo(ConstraintViolation constraintViolation) { + constraintViolation.toString(); + } + + public void foos(Set> constraintViolations) { + constraintViolations.forEach(this::foo); + } + + } + """ + ) + +// val (foreach) = acu.methodCalls().toList() + val constraintViolationT = acu.descendants(ASTClassType::class.java) + .filter { it.simpleName == "ConstraintViolation" } + .firstOrThrow().typeMirror.symbol as JClassSymbol + val (fooDecl) = acu.methodDeclarations().toList { it.symbol } + val (mref) = acu.descendants(ASTMethodReference::class.java).toList() + + acu.withTypeDsl { + mref.referencedMethod.symbol shouldBe fooDecl + + mref shouldHaveType java.util.function.Consumer::class[constraintViolationT[`?`]] + + } + } }) 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 1a6dd1488d..87f89cd75b 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 @@ -2218,4 +2218,36 @@ public class ObtainViaTest { } ]]> + + #5097 UnusedPrivateMethod with unresolved target for method reference + 0 + > listOfLists) { + listOfLists.forEach(this::doWork); + } + + //BUT this does??? + //UnusedPrivateMethod - this as a false positive - but what is different? + private void addValidationError(ConstraintViolation constraintViolation) { + constraintViolation.toString(); + } + + public void addValidationErrors(Set> constraintViolations) { + constraintViolations.forEach(this::addValidationError); + } + + } + ]]> +