Fix test with capture

This commit is contained in:
Clément Fournier committed 2022-11-29 17:25:33 +01:00
1 parent c2d738db42
commit 41bc5ac309
9 files changed
+118 -37

No files matched your search

@@ -58,7 +58,7 @@ public final class JavaAstProcessor {
static {
Level level;
try {
level = Level.valueOf(System.getenv("PMD_DEBUG_LEVEL").toLowerCase(Locale.ROOT));
level = Level.valueOf(System.getenv("PMD_DEBUG_LEVEL").toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException | NullPointerException ignored) {
level = null;
}
@@ -60,4 +60,14 @@ public class FakeSymAnnot implements SymAnnot {
public String toString() {
return "@" + annotationClass.getCanonicalName();
}
@Override
public boolean equals(Object o) {
return SymbolEquality.ANNOTATION.equals(this, o);
}
@Override
public int hashCode() {
return SymbolEquality.ANNOTATION.hash(this);
}
}
@@ -67,7 +67,11 @@ public final class TypeOps {
*/
public static boolean isSameType(JTypeMirror t, JTypeMirror s) {
return isSameType(t, s, false);
return isSameType(t, s, false, false);
}
public static boolean isSameTypeWithSameAnnotations(JTypeMirror t, JTypeMirror s) {
return isSameType(t, s, false, true);
}
/**
@@ -75,7 +79,16 @@ public final class TypeOps {
* true, then encountering inference variables produces side effects
* on them, adding bounds.
*/
public static boolean isSameType(JTypeMirror t, JTypeMirror s, boolean inInference) {
public static boolean isSameTypeInInference(JTypeMirror t, JTypeMirror s) {
return isSameType(t, s, true, false);
}
/**
* Returns true if t and s are the same type. If 'inInference' is
* true, then encountering inference variables produces side effects
* on them, adding bounds.
*/
private static boolean isSameType(JTypeMirror t, JTypeMirror s, boolean inInference, boolean considerAnnotations) {
if (t == s) {
// also returns true if both t and s are null
return true;
@@ -86,7 +99,14 @@ public final class TypeOps {
}
if (!inInference) {
return t.acceptVisitor(SameTypeVisitor.PURE, s);
if (considerAnnotations) {
if (!t.getTypeAnnotations().equals(s.getTypeAnnotations())) {
return false;
}
return t.acceptVisitor(SameTypeVisitor.PURE_WITH_ANNOTATIONS, s);
} else {
return t.acceptVisitor(SameTypeVisitor.PURE, s);
}
}
// reorder
@@ -98,34 +118,42 @@ public final class TypeOps {
}
public static boolean areSameTypes(List<JTypeMirror> ts, List<JTypeMirror> ss, boolean inInference) {
return areSameTypes(ts, ss, EMPTY, inInference);
return areSameTypes(ts, ss, EMPTY, inInference, false);
}
public static boolean areSameTypes(List<JTypeMirror> ts, List<JTypeMirror> ss, boolean inInference, boolean considerAnnotations) {
return areSameTypes(ts, ss, EMPTY, inInference, considerAnnotations);
}
public static boolean areSameTypes(List<JTypeMirror> ts, List<JTypeMirror> ss, Substitution subst) {
return areSameTypes(ts, ss, subst, false);
return areSameTypes(ts, ss, subst, false, false);
}
public static boolean areSameTypes(List<JTypeMirror> ts, List<JTypeMirror> ss, Substitution subst, boolean inInference) {
public static boolean areSameTypes(List<JTypeMirror> ts, List<JTypeMirror> ss, Substitution subst, boolean inInference, boolean considerAnnotations) {
if (ts.size() != ss.size()) {
return false;
}
for (int i = 0; i < ts.size(); i++) {
if (!isSameType(ts.get(i), ss.get(i).subst(subst), inInference)) {
if (!isSameType(ts.get(i), ss.get(i).subst(subst), inInference, considerAnnotations)) {
return false;
}
}
return true;
}
// note that this does not take type annotations into account
private static final class SameTypeVisitor implements JTypeVisitor<Boolean, JTypeMirror> {
static final SameTypeVisitor INFERENCE = new SameTypeVisitor(true);
static final SameTypeVisitor PURE = new SameTypeVisitor(false);
static final SameTypeVisitor INFERENCE = new SameTypeVisitor(true, false);
static final SameTypeVisitor PURE = new SameTypeVisitor(false, false);
static final SameTypeVisitor PURE_WITH_ANNOTATIONS = new SameTypeVisitor(false, true);
private final boolean inInference;
private final boolean considerAnnotations;
private SameTypeVisitor(boolean inInference) {
private SameTypeVisitor(boolean inInference, boolean considerAnnotations) {
this.inInference = inInference;
this.considerAnnotations = considerAnnotations;
}
@Override
@@ -145,8 +173,8 @@ public final class TypeOps {
JClassType s2 = (JClassType) s;
return t.getSymbol().equals(s2.getSymbol()) // maybe compare the type system as well.
&& t.hasErasedSuperTypes() == s2.hasErasedSuperTypes()
&& isSameType(t.getEnclosingType(), s2.getEnclosingType(), inInference)
&& areSameTypes(t.getTypeArgs(), s2.getTypeArgs(), inInference);
&& isSameType(t.getEnclosingType(), s2.getEnclosingType(), inInference, considerAnnotations)
&& areSameTypes(t.getTypeArgs(), s2.getTypeArgs(), inInference, considerAnnotations);
}
return false;
}
@@ -162,7 +190,7 @@ public final class TypeOps {
return false;
}
JWildcardType s2 = (JWildcardType) s;
return s2.isUpperBound() == t.isUpperBound() && isSameType(t.getBound(), s2.getBound(), inInference);
return s2.isUpperBound() == t.isUpperBound() && isSameType(t.getBound(), s2.getBound(), inInference, considerAnnotations);
}
@Override
@@ -204,7 +232,7 @@ public final class TypeOps {
return false;
}
if (!isSameType(t.getPrimaryBound(), s2.getPrimaryBound(), inInference)) {
if (!isSameType(t.getPrimaryBound(), s2.getPrimaryBound(), inInference, considerAnnotations)) {
return false;
}
@@ -213,7 +241,7 @@ public final class TypeOps {
boolean found = false;
for (JTypeMirror si : sComps) {
// todo won't this behaves weirdly during inference? test it
if (isSameType(ti, si, inInference)) {
if (isSameType(ti, si, inInference, considerAnnotations)) {
found = true;
break;
}
@@ -228,7 +256,7 @@ public final class TypeOps {
@Override
public Boolean visitArray(JArrayType t, JTypeMirror s) {
return s instanceof JArrayType
&& isSameType(t.getComponentType(), ((JArrayType) s).getComponentType(), inInference);
&& isSameType(t.getComponentType(), ((JArrayType) s).getComponentType(), inInference, considerAnnotations);
}
}
@@ -641,7 +669,7 @@ public final class TypeOps {
// ⊥ -------U(T)-----U(S)------> Object (L(T) = L(S) = ⊥)
// ⊥ -------L(S)-----L(T)------> Object (U(T) = U(S) = Object)
if (isSameType(s, t, true)) {
if (isSameTypeInInference(s, t)) {
// S <= S
return Convertibility.SUBTYPING;
}
@@ -727,6 +755,9 @@ public final class TypeOps {
@Override
public Convertibility visitTypeVar(JTypeVar t, JTypeMirror s) {
if (s instanceof JTypeVar && t.getSymbol() != null && Objects.equals(t.getSymbol(), s.getSymbol())) {
return Convertibility.SUBTYPING;
}
if (isTypeRange(s)) {
return isConvertible(t, lowerBoundRec(s));
}
@@ -5,7 +5,7 @@
package net.sourceforge.pmd.lang.java.types.internal.infer;
import static net.sourceforge.pmd.lang.java.types.TypeOps.isConvertible;
import static net.sourceforge.pmd.lang.java.types.TypeOps.isSameType;
import static net.sourceforge.pmd.lang.java.types.TypeOps.isSameTypeInInference;
import java.util.ArrayList;
import java.util.Set;
@@ -99,7 +99,7 @@ abstract class IncorporationAction {
*/
boolean checkBound(boolean eq, JTypeMirror t, JTypeMirror s, InferenceContext ctx) {
// eq bounds are so rare we shouldn't care if they're cached
return eq ? isSameType(t, s, true)
return eq ? isSameTypeInInference(t, s)
: checkSubtype(t, s, ctx);
}
@@ -235,7 +235,7 @@ final class PhaseOverloadSet extends OverloadSet<MethodCtDecl> {
if (TypeOps.mentionsAny(x, sfun.getTypeParameters()) && !ctx.isGround(y)) {
return false;
} else {
TypeOps.isSameType(x, y.subst(tToS), true); // adds an equality constraint
TypeOps.isSameTypeInInference(x, y.subst(tToS)); // adds an equality constraint
}
}
@@ -7,8 +7,9 @@
package net.sourceforge.pmd.lang.java.types
import io.kotest.assertions.fail
import io.kotest.assertions.withClue
import io.kotest.assertions.*
import io.kotest.assertions.print.Printed
import io.kotest.assertions.print.print
import io.kotest.matchers.shouldBe
import net.sourceforge.pmd.lang.ast.test.shouldBe
import net.sourceforge.pmd.lang.ast.test.shouldBeA
@@ -52,7 +53,16 @@ fun JTypeMirror.shouldBeUnresolvedClass(canonicalName: String) =
infix fun TypeNode.shouldHaveType(expected: JTypeMirror) {
withClue(this) {
this.typeMirror shouldBe expected
val actual = this.typeMirror
// test considering annotations
if (!isSameTypeWithSameAnnotations(actual, expected))
errorCollector.collectOrThrow(
failure(
Expected(actual.print()),
Actual(expected.print()),
)
)
}
}
@@ -9,10 +9,8 @@ package net.sourceforge.pmd.lang.java.types
import net.sourceforge.pmd.lang.java.ast.JavaNode
import net.sourceforge.pmd.lang.java.ast.ParserTestSpec
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot
import net.sourceforge.pmd.lang.java.symbols.internal.FakeSymAnnot
import net.sourceforge.pmd.lang.java.symbols.internal.TypeAnnotTestUtil
import net.sourceforge.pmd.lang.java.symbols.testdata.ClassWithTypeAnnotationsInside
import net.sourceforge.pmd.lang.java.types.TypeOps.isSameType
import kotlin.reflect.KClass
@@ -72,9 +70,9 @@ interface TypeDslMixin {
*/
val `@`: (JClassSymbol) -> SymAnnotDsl get() = { SymAnnotDsl(FakeSymAnnot(it)) }
class SymAnnotDsl(delegate: SymAnnot) : SymAnnot by delegate {
class SymAnnotDsl(private val annot: SymAnnot) {
/** An infix fun to be able to write the annotation first. */
infix fun on(t: JTypeMirror): JTypeMirror = t.addAnnotation(this)
infix fun on(t: JTypeMirror): JTypeMirror = t.addAnnotation(annot)
}
@@ -8,6 +8,7 @@ package net.sourceforge.pmd.lang.java.types.internal.infer
import net.sourceforge.pmd.lang.java.ast.*
import net.sourceforge.pmd.lang.java.types.*
import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger.VerboseLogger
import java.util.*
/**
@@ -21,11 +22,11 @@ import java.lang.annotation.*;
class Foo {
@Target(ElementType.TYPE_USE)
@interface A {}
<T> T genericMethod(T t) {
return t;
}
void someMethod(@A int i) {
var i2 = genericMethod(i);
}
@@ -51,5 +52,36 @@ class Foo {
}
}
parserTest("Test type annotations do not break wildcard capture") {
val (acu, spy) = parser.parseWithTypeInferenceSpy(
"""
import java.lang.annotation.*;
class Foo {
@Target(ElementType.TYPE_USE)
@interface A {}
interface Predicate<E> { boolean test(E e); }
<T> boolean someMethod(T t, Predicate<? super @A T> predicate) {
return predicate.test(t);
}
}
""".trimIndent()
)
val (_, A, t_Predicate) = acu.typeDeclarations().toList { it.symbol }
spy.shouldBeOk {
val `@A` = `@`(A)
acu.firstMethodCall() shouldHaveType boolean
acu.firstMethodCall().methodType.shouldMatchMethod(
named = "test",
declaredIn = t_Predicate[captureMatcher( `?` `super` (`@A` on acu.typeVar("T")))]
)
}
}
})
@@ -153,20 +153,20 @@
| | | | +- WildcardType[@TypeMirror = "? extends T"]
| | | | +- ClassOrInterfaceType[@TypeMirror = "T"]
| | | +- VariableDeclaratorId[@Name = "iter", @TypeMirror = "java.util.Iterator<? extends T>"]
| | +- FormalParameter[@TypeMirror = "java.util.function.Function<? super T, ? extends java.util.Iterator<? extends R>>"]
| | +- FormalParameter[@TypeMirror = "java.util.function.Function<? super T, ? extends @Nullable java.util.Iterator<? extends R>>"]
| | +- ModifierList[]
| | +- ClassOrInterfaceType[@TypeMirror = "java.util.function.Function<? super T, ? extends java.util.Iterator<? extends R>>"]
| | +- ClassOrInterfaceType[@TypeMirror = "java.util.function.Function<? super T, ? extends @Nullable java.util.Iterator<? extends R>>"]
| | | +- TypeArguments[]
| | | +- WildcardType[@TypeMirror = "? super T"]
| | | | +- ClassOrInterfaceType[@TypeMirror = "T"]
| | | +- WildcardType[@TypeMirror = "? extends java.util.Iterator<? extends R>"]
| | | +- ClassOrInterfaceType[@TypeMirror = "java.util.Iterator<? extends R>"]
| | | +- WildcardType[@TypeMirror = "? extends @Nullable java.util.Iterator<? extends R>"]
| | | +- ClassOrInterfaceType[@TypeMirror = "@Nullable java.util.Iterator<? extends R>"]
| | | +- Annotation[@TypeMirror = "*Nullable"]
| | | | +- ClassOrInterfaceType[@TypeMirror = "*Nullable"]
| | | +- TypeArguments[]
| | | +- WildcardType[@TypeMirror = "? extends R"]
| | | +- ClassOrInterfaceType[@TypeMirror = "R"]
| | +- VariableDeclaratorId[@Name = "f", @TypeMirror = "java.util.function.Function<? super T, ? extends java.util.Iterator<? extends R>>"]
| | +- VariableDeclaratorId[@Name = "f", @TypeMirror = "java.util.function.Function<? super T, ? extends @Nullable java.util.Iterator<? extends R>>"]
| +- Block[]
| +- ReturnStatement[]
| +- ConstructorCall[@Failed = false, @Function = "net.sourceforge.pmd.internal.util.IteratorUtilCopy$AbstractIterator<R>.new() -> net.sourceforge.pmd.internal.util.IteratorUtilCopy$AbstractIterator<R>", @MethodName = "new", @TypeMirror = "net.sourceforge.pmd.internal.util.IteratorUtilCopy$AbstractIterator<R>", @Unchecked = false, @VarargsCall = false]
@@ -222,8 +222,8 @@
| | | | +- ClassOrInterfaceType[@TypeMirror = "R"]
| | | +- VariableDeclarator[@TypeMirror = "void"]
| | | +- VariableDeclaratorId[@Name = "next", @TypeMirror = "java.util.Iterator<? extends R>"]
| | | +- MethodCall[@Failed = false, @Function = "java.util.function.Function<capture#... of ? super T, capture#... of ? extends java.util.Iterator<? extends R>>.apply(capture#... of ? super T) -> capture#... of ? extends java.util.Iterator<? extends R>", @MethodName = "apply", @TypeMirror = "capture#... of ? extends java.util.Iterator<? extends R>", @Unchecked = false, @VarargsCall = false]
| | | +- VariableAccess[@Name = "f", @TypeMirror = "java.util.function.Function<capture#... of ? super T, capture#... of ? extends java.util.Iterator<? extends R>>"]
| | | +- MethodCall[@Failed = true, @Function = "(*unknown*).(*unknown method*)() -> (*unknown*)", @MethodName = "apply", @TypeMirror = "(*unknown*)", @Unchecked = false, @VarargsCall = false]
| | | +- VariableAccess[@Name = "f", @TypeMirror = "java.util.function.Function<capture#... of ? super T, capture#... of ? extends @Nullable java.util.Iterator<? extends R>>"]
| | | +- ArgumentList[]
| | | +- MethodCall[@Failed = false, @Function = "java.util.Iterator<capture#... of ? extends T>.next() -> capture#... of ? extends T", @MethodName = "next", @TypeMirror = "capture#... of ? extends T", @Unchecked = false, @VarargsCall = false]
| | | +- VariableAccess[@Name = "iter", @TypeMirror = "java.util.Iterator<capture#... of ? extends T>"]