Correct substitutions for smart completion after "by" and "in"
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
package org.jetbrains.kotlin.idea.util
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.CallHandle
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl
|
||||
@@ -56,15 +57,26 @@ class FuzzyType(
|
||||
|
||||
init {
|
||||
if (freeParameters.isNotEmpty()) {
|
||||
val usedTypeParameters = HashSet<TypeParameterDescriptor>()
|
||||
usedTypeParameters.addUsedTypeParameters(type)
|
||||
this.freeParameters = freeParameters.filter { it in usedTypeParameters }.toSet()
|
||||
// we allow to pass type parameters from another function with the same original in freeParameters
|
||||
val usedTypeParameters = HashSet<TypeParameterDescriptor>().apply { addUsedTypeParameters(type) }
|
||||
if (usedTypeParameters.isNotEmpty()) {
|
||||
val originalFreeParameters = freeParameters.map { it.toOriginal() }.toSet()
|
||||
this.freeParameters = usedTypeParameters.filter { it.toOriginal() in originalFreeParameters }.toSet()
|
||||
}
|
||||
else {
|
||||
this.freeParameters = emptySet()
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.freeParameters = emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
private fun TypeParameterDescriptor.toOriginal(): TypeParameterDescriptor {
|
||||
val functionDescriptor = containingDeclaration as? FunctionDescriptor ?: return this
|
||||
return functionDescriptor.original.typeParameters[index]
|
||||
}
|
||||
|
||||
override fun equals(other: Any?) = other is FuzzyType && other.type == type && other.freeParameters == freeParameters
|
||||
|
||||
override fun hashCode() = type.hashCode()
|
||||
@@ -137,9 +149,9 @@ class FuzzyType(
|
||||
// that's why we have to check subtyping manually
|
||||
val substitutor = constraintSystem.resultingSubstitutor
|
||||
val substitutedType = substitutor.substitute(type, Variance.INVARIANT) ?: return null
|
||||
if (substitutedType.isError) return null
|
||||
if (substitutedType.isError) return TypeSubstitutor.EMPTY
|
||||
val otherSubstitutedType = substitutor.substitute(otherType.type, Variance.INVARIANT) ?: return null
|
||||
if (otherSubstitutedType.isError) return null
|
||||
if (otherSubstitutedType.isError) return TypeSubstitutor.EMPTY
|
||||
if (!substitutedType.checkInheritance(otherSubstitutedType)) return null
|
||||
|
||||
val substitution = constraintSystem.typeVariables.map { it.originalTypeParameter }.associateBy({ it.typeConstructor }) {
|
||||
|
||||
@@ -11,7 +11,7 @@ fun foo(a: A, cA: Collection<A>, cB: Collection<B>, cC: Collection<C>, cAny: Co
|
||||
}
|
||||
|
||||
// EXIST: cA
|
||||
// ABSENT: cB
|
||||
// EXIST: cB
|
||||
// ABSENT: cC
|
||||
// EXIST: cAny
|
||||
// EXIST: lA
|
||||
@@ -19,6 +19,6 @@ fun foo(a: A, cA: Collection<A>, cB: Collection<B>, cC: Collection<C>, cAny: Co
|
||||
// ABSENT: lC
|
||||
// EXIST: lAny
|
||||
// EXIST: aA
|
||||
// ABSENT: aB
|
||||
// EXIST: aB
|
||||
// ABSENT: aC
|
||||
// EXIST: aAny
|
||||
|
||||
@@ -2,4 +2,4 @@ fun foo(s: String) {
|
||||
if (s in <caret>)
|
||||
}
|
||||
|
||||
// EXIST: { lookupString:"listOf", itemText: "listOf", tailText: "(vararg elements: T) (kotlin.collections)", typeText:"List<T>" }
|
||||
// EXIST: { lookupString:"listOf", itemText: "listOf", tailText: "(vararg elements: String) (kotlin.collections)", typeText:"List<String>" }
|
||||
|
||||
@@ -10,4 +10,4 @@ interface A {
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: { lookupString:"createX", itemText: "createX", tailText: "(t: T)", typeText:"X<T>" }
|
||||
// EXIST: { lookupString:"createX", itemText: "createX", tailText: "(t: String)", typeText:"X<String>" }
|
||||
|
||||
@@ -10,4 +10,4 @@ interface A {
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: { lookupString:"createX", itemText: "createX", tailText: "(t: T)", typeText:"X<T>" }
|
||||
// EXIST: { lookupString:"createX", itemText: "createX", tailText: "(t: String)", typeText:"X<String>" }
|
||||
|
||||
@@ -4,7 +4,7 @@ class C {
|
||||
val v by Delegates.<caret>
|
||||
}
|
||||
|
||||
// EXIST: notNull
|
||||
// EXIST: observable
|
||||
// EXIST: vetoable
|
||||
// EXIST: { itemText: "notNull", typeText: "ReadWriteProperty<Any?, T>" }
|
||||
// EXIST: { itemText: "observable", typeText: "ReadWriteProperty<Any?, T>" }
|
||||
// EXIST: { itemText: "vetoable", typeText: "ReadWriteProperty<Any?, T>" }
|
||||
// NOTHING_ELSE
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class C {
|
||||
val v: String by Delegates.<caret>
|
||||
}
|
||||
|
||||
// EXIST: { itemText: "notNull", typeText: "ReadWriteProperty<Any?, String>" }
|
||||
// EXIST: { itemText: "observable", typeText: "ReadWriteProperty<Any?, String>" }
|
||||
// EXIST: { itemText: "vetoable", typeText: "ReadWriteProperty<Any?, String>" }
|
||||
// NOTHING_ELSE
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class Property<TOwner, TValue>
|
||||
|
||||
operator fun <TValue, TOwner> Property<TOwner, TValue>.getValue(thisRef: TOwner, property: KProperty<*>): TValue {
|
||||
throw Exception()
|
||||
}
|
||||
|
||||
fun<TOwner, TValue> createProperty(): Property<TOwner, TValue> = Property()
|
||||
|
||||
class C {
|
||||
val v by create<caret>
|
||||
}
|
||||
|
||||
// EXIST: { itemText: "createProperty", typeText: "Property<C, TValue>" }
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class Property<TOwner, TValue>
|
||||
|
||||
operator fun <TValue, TOwner> Property<TOwner, TValue>.getValue(thisRef: TOwner, property: KProperty<*>): TValue {
|
||||
throw Exception()
|
||||
}
|
||||
|
||||
fun<TOwner, TValue> createProperty(): Property<TOwner, TValue> = Property()
|
||||
|
||||
class C {
|
||||
val v: Int by create<caret>
|
||||
}
|
||||
|
||||
// EXIST: { itemText: "createProperty", typeText: "Property<C, Int>" }
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class Property<TOwner1, TValue1>
|
||||
|
||||
operator fun <TValue2, TOwner2> Property<TOwner2, TValue2>.getValue(thisRef: TOwner2, property: KProperty<*>): TValue2 {
|
||||
throw Exception()
|
||||
}
|
||||
|
||||
operator fun <TValue3, TOwner3> Property<TOwner3, TValue3>.setValue(thisRef: TOwner3, property: KProperty<*>, value: TValue3) {
|
||||
throw Exception()
|
||||
}
|
||||
|
||||
fun<TOwner4, TValue4> createProperty(): Property<TOwner4, TValue4> = Property()
|
||||
|
||||
class C {
|
||||
var v by create<caret>
|
||||
}
|
||||
|
||||
// EXIST: { itemText: "createProperty", typeText: "Property<C, TValue4>" }
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class Property<TOwner1, TValue1>
|
||||
|
||||
operator fun <TValue2, TOwner2> Property<TOwner2, TValue2>.getValue(thisRef: TOwner2, property: KProperty<*>): TValue2 {
|
||||
throw Exception()
|
||||
}
|
||||
|
||||
operator fun <TValue3, TOwner3> Property<TOwner3, TValue3>.setValue(thisRef: TOwner3, property: KProperty<*>, value: TValue3) {
|
||||
throw Exception()
|
||||
}
|
||||
|
||||
fun<TOwner4, TValue4> createProperty(): Property<TOwner4, TValue4> = Property()
|
||||
|
||||
class C {
|
||||
var v: Int by create<caret>
|
||||
}
|
||||
|
||||
// EXIST: { itemText: "createProperty", typeText: "Property<C, Int>" }
|
||||
+30
@@ -1450,6 +1450,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("DelegatesDotExplicitPropertyType.kt")
|
||||
public void testDelegatesDotExplicitPropertyType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/DelegatesDotExplicitPropertyType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExplicitValType.kt")
|
||||
public void testExplicitValType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ExplicitValType.kt");
|
||||
@@ -1462,6 +1468,30 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionSubstitution1.kt")
|
||||
public void testExtensionSubstitution1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionSubstitution2.kt")
|
||||
public void testExtensionSubstitution2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionSubstitution3.kt")
|
||||
public void testExtensionSubstitution3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution3.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionSubstitution4.kt")
|
||||
public void testExtensionSubstitution4() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution4.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionVal.kt")
|
||||
public void testExtensionVal() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ExtensionVal.kt");
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
|
||||
@@ -592,7 +593,7 @@ class ExpectedInfos(
|
||||
|
||||
val byTypeFilter = object : ByTypeFilter {
|
||||
override fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? {
|
||||
return if (detector.findOperator(descriptorType) != null) TypeSubstitutor.EMPTY else null
|
||||
return detector.findOperator(descriptorType)?.second
|
||||
}
|
||||
}
|
||||
return listOf(ExpectedInfo(byTypeFilter, null, null))
|
||||
@@ -614,16 +615,22 @@ class ExpectedInfos(
|
||||
|
||||
val byTypeFilter = object : ByTypeFilter {
|
||||
override fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? {
|
||||
val getValueOperator = typesWithGetDetector.findOperator(descriptorType) ?: return null
|
||||
val (getValueOperator, getOperatorSubstitutor) = typesWithGetDetector.findOperator(descriptorType) ?: return null
|
||||
|
||||
if (typesWithSetDetector != null) {
|
||||
val setValueOperator = typesWithSetDetector.findOperator(descriptorType) ?: return null
|
||||
val propertyType = explicitPropertyType ?: getValueOperator.returnType!!
|
||||
val setParamType = FuzzyType(setValueOperator.valueParameters.last().type, setValueOperator.typeParameters)
|
||||
if (setParamType.checkIsSuperTypeOf(propertyType) == null) return null
|
||||
}
|
||||
if (typesWithSetDetector == null) return getOperatorSubstitutor
|
||||
|
||||
return TypeSubstitutor.EMPTY //TODO: substitutor from property type
|
||||
val substitutedType = FuzzyType(getOperatorSubstitutor.substitute(descriptorType.type, Variance.INVARIANT)!!, descriptorType.freeParameters)
|
||||
|
||||
val (setValueOperator, setOperatorSubstitutor) = typesWithSetDetector.findOperator(substitutedType) ?: return null
|
||||
val propertyType = if (explicitPropertyType != null)
|
||||
FuzzyType(explicitPropertyType, emptyList())
|
||||
else
|
||||
getValueOperator.fuzzyReturnType()!!
|
||||
val setParamType = FuzzyType(setValueOperator.valueParameters.last().type, setValueOperator.typeParameters)
|
||||
val setParamTypeSubstitutor = setParamType.checkIsSuperTypeOf(propertyType) ?: return null
|
||||
return TypeSubstitutor.createChainedSubstitutor(getOperatorSubstitutor.substitution,
|
||||
TypeSubstitutor.createChainedSubstitutor(setOperatorSubstitutor.substitution,
|
||||
setParamTypeSubstitutor.substitution).substitution)
|
||||
}
|
||||
}
|
||||
return listOf(ExpectedInfo(byTypeFilter, null, null))
|
||||
|
||||
@@ -19,12 +19,14 @@ package org.jetbrains.kotlin.idea.core
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
|
||||
import org.jetbrains.kotlin.idea.util.nullability
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.collectFunctions
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.util.isValidOperator
|
||||
@@ -35,26 +37,26 @@ abstract class TypesWithOperatorDetector(
|
||||
private val scope: LexicalScope,
|
||||
private val indicesHelper: KotlinIndicesHelper?
|
||||
) {
|
||||
protected abstract fun isSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean
|
||||
protected abstract fun checkIsSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): TypeSubstitutor?
|
||||
|
||||
private val cache = HashMap<FuzzyType, FunctionDescriptor?>()
|
||||
private val cache = HashMap<FuzzyType, Pair<FunctionDescriptor, TypeSubstitutor>?>()
|
||||
|
||||
private val typesWithExtensionFromScope: Map<KotlinType, FunctionDescriptor> by lazy {
|
||||
scope.collectFunctions(name, NoLookupLocation.FROM_IDE)
|
||||
.filter { it.extensionReceiverParameter != null && it.isValidOperator() && isSuitableByType(it, it.typeParameters) }
|
||||
.map { it.extensionReceiverParameter!!.type to it }
|
||||
.toMap()
|
||||
private val extensionOperators: Collection<FunctionDescriptor> by lazy {
|
||||
val result = ArrayList<FunctionDescriptor>()
|
||||
collectExtensionOperators(scope.collectFunctions(name, NoLookupLocation.FROM_IDE), result)
|
||||
indicesHelper?.getTopLevelExtensionOperatorsByName(name.asString())?.let { collectExtensionOperators(it, result) }
|
||||
result.distinctBy { it.original }
|
||||
}
|
||||
|
||||
private val typesWithExtensionFromIndices: Map<KotlinType, FunctionDescriptor> by lazy {
|
||||
indicesHelper?.getTopLevelExtensionOperatorsByName(name.asString())
|
||||
?.filter { it.extensionReceiverParameter != null && it.isValidOperator() && isSuitableByType(it, it.typeParameters) }
|
||||
?.map { it.extensionReceiverParameter!!.type to it }
|
||||
?.filter { it.first !in typesWithExtensionFromScope.keys }
|
||||
?.toMap() ?: emptyMap()
|
||||
private fun collectExtensionOperators(functions: Collection<FunctionDescriptor>, result: MutableCollection<FunctionDescriptor>) {
|
||||
for (function in functions) {
|
||||
if (function.extensionReceiverParameter == null || !function.isValidOperator()) continue
|
||||
val substitutor = checkIsSuitableByType(function, function.typeParameters) ?: continue
|
||||
result.add(function.substitute(substitutor))
|
||||
}
|
||||
}
|
||||
|
||||
fun findOperator(type: FuzzyType): FunctionDescriptor? {
|
||||
fun findOperator(type: FuzzyType): Pair<FunctionDescriptor, TypeSubstitutor>? {
|
||||
if (cache.containsKey(type)) {
|
||||
return cache[type]
|
||||
}
|
||||
@@ -65,16 +67,21 @@ abstract class TypesWithOperatorDetector(
|
||||
}
|
||||
}
|
||||
|
||||
private fun findOperatorNoCache(type: FuzzyType): FunctionDescriptor? {
|
||||
private fun findOperatorNoCache(type: FuzzyType): Pair<FunctionDescriptor, TypeSubstitutor>? {
|
||||
if (type.nullability() != TypeNullability.NULLABLE) {
|
||||
val memberFunction = type.type.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_IDE)
|
||||
.firstOrNull { it.isValidOperator() && isSuitableByType(it, type.freeParameters) }
|
||||
if (memberFunction != null) return memberFunction
|
||||
for (memberFunction in type.type.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_IDE)) {
|
||||
if (memberFunction.isValidOperator()) {
|
||||
checkIsSuitableByType(memberFunction, type.freeParameters)?.let { substitutor ->
|
||||
return Pair(memberFunction.substitute(substitutor), substitutor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ((typeWithExtension, operator) in typesWithExtensionFromScope + typesWithExtensionFromIndices) {
|
||||
if (type.checkIsSubtypeOf(typeWithExtension) != null) {
|
||||
return operator //TODO: substitution
|
||||
for (operator in extensionOperators) {
|
||||
val substitutor = type.checkIsSubtypeOf(operator.fuzzyExtensionReceiverType()!!)
|
||||
if (substitutor != null) {
|
||||
return Pair(operator.substitute(substitutor), substitutor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,10 +95,10 @@ class TypesWithContainsDetector(
|
||||
private val argumentType: KotlinType
|
||||
) : TypesWithOperatorDetector(OperatorNameConventions.CONTAINS, scope, indicesHelper) {
|
||||
|
||||
override fun isSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
|
||||
override fun checkIsSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): TypeSubstitutor? {
|
||||
val parameter = function.valueParameters.single()
|
||||
val fuzzyParameterType = FuzzyType(parameter.type, function.typeParameters + freeTypeParams)
|
||||
return fuzzyParameterType.checkIsSuperTypeOf(argumentType) != null
|
||||
return fuzzyParameterType.checkIsSuperTypeOf(argumentType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,16 +109,15 @@ class TypesWithGetValueDetector(
|
||||
private val propertyType: KotlinType?
|
||||
) : TypesWithOperatorDetector(OperatorNameConventions.GET_VALUE, scope, indicesHelper) {
|
||||
|
||||
override fun isSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
|
||||
override fun checkIsSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): TypeSubstitutor? {
|
||||
val paramType = FuzzyType(function.valueParameters.first().type, freeTypeParams)
|
||||
if (paramType.checkIsSuperTypeOf(propertyOwnerType) == null) return false
|
||||
val substitutor = paramType.checkIsSuperTypeOf(propertyOwnerType) ?: return null
|
||||
|
||||
if (propertyType != null) {
|
||||
val returnType = FuzzyType(function.returnType ?: return false, freeTypeParams)
|
||||
return returnType.checkIsSubtypeOf(propertyType) != null
|
||||
}
|
||||
if (propertyType == null) return substitutor
|
||||
|
||||
return true
|
||||
val fuzzyReturnType = FuzzyType(function.returnType ?: return null, freeTypeParams)
|
||||
val substitutorFromPropertyType = fuzzyReturnType.checkIsSubtypeOf(propertyType) ?: return null
|
||||
return TypeSubstitutor.createChainedSubstitutor(substitutor.substitution, substitutorFromPropertyType.substitution)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,8 +127,8 @@ class TypesWithSetValueDetector(
|
||||
private val propertyOwnerType: KotlinType
|
||||
) : TypesWithOperatorDetector(OperatorNameConventions.SET_VALUE, scope, indicesHelper) {
|
||||
|
||||
override fun isSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
|
||||
override fun checkIsSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): TypeSubstitutor? {
|
||||
val paramType = FuzzyType(function.valueParameters.first().type, freeTypeParams)
|
||||
return paramType.checkIsSuperTypeOf(propertyOwnerType) != null
|
||||
return paramType.checkIsSuperTypeOf(propertyOwnerType)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user