Try to fix bug with capture

Spot a bug with nested expressions. Skipping
instantiation is disabled until later
This commit is contained in:
Clément Fournier
2020-08-11 19:30:56 +02:00
parent bf1bfdcd38
commit f740bb115b
7 changed files with 97 additions and 25 deletions

View File

@ -382,25 +382,20 @@ public final class TypeOps {
private static JTypeMirror upperBound(JTypeMirror type) {
if (type instanceof JWildcardType) {
return upperBound(((JWildcardType) type).asUpperBound());
} else if (type instanceof JTypeVar && ((JTypeVar) type).isCaptured()) {
return upperBound(((JTypeVar) type).getUpperBound());
return ((JWildcardType) type).asUpperBound();
}
return type;
}
private static JTypeMirror lowerBound(JTypeMirror type) {
if (type instanceof JWildcardType) {
return lowerBound(((JWildcardType) type).asLowerBound());
} else if (type instanceof JTypeVar && ((JTypeVar) type).isCaptured()) {
return lowerBound(((JTypeVar) type).getLowerBound());
return ((JWildcardType) type).asLowerBound();
}
return type;
}
private static boolean isTypeRange(JTypeMirror s) {
return s instanceof JWildcardType
|| s instanceof JTypeVar && ((JTypeVar) s).isCaptured();
return s instanceof JWildcardType;
}

View File

@ -97,11 +97,11 @@ final class ExprCheckHelper {
// now if the return type of the arg is polymorphic and unsolved,
// there are some additional bounds on our own infCtx
// FIXME if the argument is not compatible we'll fail resolution
// altogether, though if we're in invocation phase we should
// preserve the CT-decl
checker.checkExprConstraint(infCtx, mostSpecific.getReturnType(), targetType);
// fixme this fails to set the inferred type of arguments
// if we skip invocation on the outer expr
if (phase.isInvocation()) {
infCtx.addInstantiationListener(
infCtx.freeVarsIn(mostSpecific.getReturnType()),

View File

@ -448,7 +448,7 @@ final class ExprOps {
TypeSystem ts = sig.getTypeSystem();
if ("getClass".equals(sig.getName()) && sig.getDeclaringType().equals(ts.OBJECT)) {
if (erasedReceiverType != null) {
return sig.internalApi().withReturnType(getClassReturn(erasedReceiverType, ts));
return sig.internalApi().withReturnType(getClassReturn(erasedReceiverType, ts)).internalApi().markAsAdapted();
}
}
return sig;

View File

@ -247,13 +247,11 @@ public final class Infer {
if (isReturnTypeFinished(m)) {
assert assertReturnIsGround(m);
// todo this appears duplicated
m = ExprOps.adaptGetClass(m, site.getExpr().getErasedReceiverType());
LOG.skipInstantiation(m, site);
expr.setInferredType(m.getReturnType());
if (site.areAllArgsRelevantToApplicability()) {
// fixme the type of subexpressions might not have been set
if (false && site.areAllArgsRelevantToApplicability()) {
// then all have been inferred
return ctdecl;
}

View File

@ -6,11 +6,13 @@
package net.sourceforge.pmd.lang.java.types
import io.kotest.matchers.shouldBe
import io.kotest.property.Arb
import io.kotest.property.Exhaustive
import io.kotest.property.RandomSource
import io.kotest.property.Sample
import net.sourceforge.pmd.lang.ast.test.shouldBe
import net.sourceforge.pmd.lang.ast.test.shouldBeA
import net.sourceforge.pmd.lang.java.ast.ASTTypeParameter
import net.sourceforge.pmd.lang.java.ast.ASTTypeParameters
import net.sourceforge.pmd.lang.java.ast.JavaNode
@ -59,6 +61,14 @@ fun JTypeVar.withNewBounds(upper: JTypeMirror? = null, lower:JTypeMirror? = null
this.cloneWithBounds(upper ?: this.upperBound, lower ?: this.lowerBound)
}
fun JTypeMirror.shouldBeCaptureOf(wild: JWildcardType) =
this.shouldBeA<JTypeVar> {
it.isCaptured shouldBe true
if (wild.isLowerBound)
it.lowerBound shouldBe wild.asLowerBound()
else
it.upperBound shouldBe wild.asUpperBound()
}
@Suppress("ObjectPropertyName", "MemberVisibilityCanBePrivate")
class TypeGen(override val ts: TypeSystem) : Arb<JTypeMirror>(), TypeDslMixin {
@ -125,6 +135,7 @@ class TypeGen(override val ts: TypeSystem) : Arb<JTypeMirror>(), TypeDslMixin {
/** raw Comparable */
val t_Comparable: JClassType get() = java.lang.Comparable::class.raw
val t_Comparator: JClassType get() = java.util.Comparator::class.raw
val t_EnumSet: JClassType get() = java.util.EnumSet::class.raw

View File

@ -4,12 +4,16 @@
package net.sourceforge.pmd.lang.java.types.internal.infer
import io.kotest.inspectors.forExactly
import io.kotest.matchers.collections.shouldBeSingleton
import io.kotest.matchers.shouldBe
import net.sourceforge.pmd.lang.ast.test.shouldBe
import net.sourceforge.pmd.lang.ast.test.shouldBeA
import net.sourceforge.pmd.lang.ast.test.shouldMatchN
import net.sourceforge.pmd.lang.java.ast.*
import net.sourceforge.pmd.lang.java.types.*
import net.sourceforge.pmd.lang.java.types.testdata.TypeInferenceTestCases
import net.sourceforge.pmd.lang.java.types.typeDsl
import java.util.function.ToIntFunction
/**
* @author Clément Fournier
@ -83,6 +87,75 @@ class CaptureInferenceTest : ProcessorTestSpec({
}
}
parserTest("Test method ref on captured thing") {
logTypeInference(verbose = true)
val acu = parser.parse("""
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
class Scratch {
private List<? extends String> sortIt(final List<? extends String> stats) {
final List<? extends String> statList = new ArrayList<>(stats);
statList.sort(Comparator.comparingInt(Object::hashCode));
return statList;
}
}
""".trimIndent())
val call = acu.descendants(ASTMethodCall::class.java).first()!!
call.shouldMatchN {
methodCall("sort") {
it::getTypeMirror shouldBe with(it.typeDsl) { ts.NO_TYPE }
variableAccess("statList") {}
argList {
var capture: JTypeVar? = null
methodCall("comparingInt") {
with (it.typeDsl) {
// eg. java.util.Comparator<capture#45 of ? extends java.lang.String>
val ret = it.typeMirror.shouldBeA<JClassType> {
it.symbol shouldBe gen.t_Comparator.symbol
it.typeArgs.shouldBeSingleton {
capture = it.shouldBeCaptureOf(`?` extends gen.t_String)
}
}
it.methodType.shouldMatchMethod(
named = "comparingInt",
declaredIn = gen.t_Comparator,
withFormals = listOf(ToIntFunction::class[`?` `super` capture!!]),
returning = ret
)
}
typeExpr {
classType("Comparator")
}
argList {
methodRef("hashCode") {
typeExpr {
classType("Object")
}
with(it.typeDsl) {
it.referencedMethod shouldBe ts.OBJECT.getMethodsByName("hashCode").single()
it.typeMirror shouldBe ToIntFunction::class[capture!!]
}
}
}
}
}
}
}
}
})
/**

View File

@ -4,16 +4,11 @@
package net.sourceforge.pmd.lang.ast.test
import kotlin.reflect.KCallable
import kotlin.reflect.jvm.isAccessible
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.shouldBe as ktShouldBe
import io.kotest.matchers.should
import io.kotest.matchers.Matcher
import io.kotest.matchers.equalityMatcher
import io.kotest.matchers.collections.haveSize
import java.util.stream.Stream
import kotlin.streams.toList
import io.kotest.matchers.should
import kotlin.reflect.KCallable
import kotlin.reflect.jvm.isAccessible
/**
* Extension to add the name of a property to error messages.