diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 9403457ac1..4469d3528b 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -27,6 +27,7 @@ This is a {{ site.pmd.release_type }} release. * [#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 + * [#5097](https://github.com/pmd/pmd/issues/5097): \[java] UnusedPrivateMethod FP with raw type missing from the classpath * [#5318](https://github.com/pmd/pmd/issues/5318): \[java] PreserveStackTraceRule: false-positive on Pattern Matching with instanceof * java-performance * [#5287](https://github.com/pmd/pmd/issues/5287): \[java] TooFewBranchesForSwitch false-positive with switch using list of case constants 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 7a8b14a2af..7831441b3a 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 @@ -704,4 +704,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 548b16a161..f0146762a8 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,6 +2218,38 @@ 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); + } + + } + ]]> + UnusedPrivateMethod #5113