Initial implementation of smart completion after "by"
#KT-11787 Fixed
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class X1 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): CharSequence = ""
|
||||||
|
}
|
||||||
|
class X2 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
}
|
||||||
|
class X3 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): Any = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
class Y1
|
||||||
|
class Y2
|
||||||
|
|
||||||
|
operator fun Y1.getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
operator fun Y2.getValue(thisRef: C, property: KProperty<*>): Int = 1
|
||||||
|
|
||||||
|
fun createX1() = X1()
|
||||||
|
fun createX2() = X2()
|
||||||
|
fun createX3() = X3()
|
||||||
|
fun createY1() = Y1()
|
||||||
|
fun createY2() = Y2()
|
||||||
|
|
||||||
|
class C {
|
||||||
|
val property: CharSequence by <caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: lazy
|
||||||
|
// EXIST: createX1
|
||||||
|
// EXIST: createX2
|
||||||
|
// ABSENT: createX3
|
||||||
|
// EXIST: createY1
|
||||||
|
// ABSENT: createY2
|
||||||
|
/*TODO: add constructors*/
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class X1 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
operator fun setValue(thisRef: C, property: KProperty<*>, value: String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class X2 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
operator fun setValue(thisRef: C, property: KProperty<*>, value: CharSequence) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class X3 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): CharSequence = ""
|
||||||
|
operator fun setValue(thisRef: C, property: KProperty<*>, value: String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createX1() = X1()
|
||||||
|
fun createX2() = X2()
|
||||||
|
fun createX3() = X3()
|
||||||
|
|
||||||
|
class C {
|
||||||
|
var property: CharSequence by <caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// ABSENT: lazy
|
||||||
|
// ABSENT: createX1
|
||||||
|
// EXIST: createX2
|
||||||
|
// ABSENT: createX3
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class X1 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
}
|
||||||
|
class X2 {
|
||||||
|
operator fun getValue(thisRef: String, property: KProperty<*>): String = ""
|
||||||
|
}
|
||||||
|
class X3 {
|
||||||
|
operator fun getValue(thisRef: Any, property: KProperty<*>): String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
class Y1
|
||||||
|
class Y2
|
||||||
|
class Y3
|
||||||
|
|
||||||
|
operator fun Y1.getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
operator fun Y2.getValue(thisRef: String, property: KProperty<*>): String = ""
|
||||||
|
operator fun Y3.getValue(thisRef: Any, property: KProperty<*>): String = ""
|
||||||
|
|
||||||
|
fun createX1() = X1()
|
||||||
|
fun createX2() = X2()
|
||||||
|
fun createX3() = X3()
|
||||||
|
fun createY1() = Y1()
|
||||||
|
fun createY2() = Y2()
|
||||||
|
fun createY3() = Y3()
|
||||||
|
|
||||||
|
class C
|
||||||
|
|
||||||
|
val C.property by <caret>
|
||||||
|
|
||||||
|
// EXIST: lazy
|
||||||
|
// EXIST: createX1
|
||||||
|
// ABSENT: createX2
|
||||||
|
// EXIST: createX3
|
||||||
|
// EXIST: createY1
|
||||||
|
// ABSENT: createY2
|
||||||
|
// EXIST: createY3
|
||||||
|
/*TODO: add constructors*/
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class X1 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
operator fun setValue(thisRef: C, property: KProperty<*>, value: String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class X2 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
class X3 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
operator fun setValue(thisRef: String, property: KProperty<*>, value: String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class X4 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
operator fun setValue(thisRef: C, property: KProperty<*>, value: CharSequence) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class X5 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): CharSequence = ""
|
||||||
|
operator fun setValue(thisRef: C, property: KProperty<*>, value: String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createX1() = X1()
|
||||||
|
fun createX2() = X2()
|
||||||
|
fun createX3() = X3()
|
||||||
|
fun createX4() = X4()
|
||||||
|
fun createX5() = X5()
|
||||||
|
|
||||||
|
class C
|
||||||
|
|
||||||
|
var C.property by <caret>
|
||||||
|
|
||||||
|
// ABSENT: lazy
|
||||||
|
// EXIST: createX1
|
||||||
|
// ABSENT: createX2
|
||||||
|
// ABSENT: createX3
|
||||||
|
// EXIST: createX4
|
||||||
|
// ABSENT: createX5
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class X1 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
}
|
||||||
|
class X2 {
|
||||||
|
operator fun getValue(thisRef: String, property: KProperty<*>): String = ""
|
||||||
|
}
|
||||||
|
class X3 {
|
||||||
|
operator fun getValue(thisRef: Any, property: KProperty<*>): String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
class Y1
|
||||||
|
class Y2
|
||||||
|
class Y3
|
||||||
|
|
||||||
|
operator fun Y1.getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
operator fun Y2.getValue(thisRef: String, property: KProperty<*>): String = ""
|
||||||
|
operator fun Y3.getValue(thisRef: Any, property: KProperty<*>): String = ""
|
||||||
|
|
||||||
|
fun createX1() = X1()
|
||||||
|
fun createX2() = X2()
|
||||||
|
fun createX3() = X3()
|
||||||
|
fun createY1() = Y1()
|
||||||
|
fun createY2() = Y2()
|
||||||
|
fun createY3() = Y3()
|
||||||
|
|
||||||
|
class C {
|
||||||
|
val property by <caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: lazy
|
||||||
|
// EXIST: createX1
|
||||||
|
// ABSENT: createX2
|
||||||
|
// EXIST: createX3
|
||||||
|
// EXIST: createY1
|
||||||
|
// ABSENT: createY2
|
||||||
|
// EXIST: createY3
|
||||||
|
/*TODO: add constructors*/
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class X1 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
operator fun setValue(thisRef: C, property: KProperty<*>, value: String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class X2 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
class X3 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
operator fun setValue(thisRef: String, property: KProperty<*>, value: String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class X4 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
|
||||||
|
operator fun setValue(thisRef: C, property: KProperty<*>, value: CharSequence) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class X5 {
|
||||||
|
operator fun getValue(thisRef: C, property: KProperty<*>): CharSequence = ""
|
||||||
|
operator fun setValue(thisRef: C, property: KProperty<*>, value: String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createX1() = X1()
|
||||||
|
fun createX2() = X2()
|
||||||
|
fun createX3() = X3()
|
||||||
|
fun createX4() = X4()
|
||||||
|
fun createX5() = X5()
|
||||||
|
|
||||||
|
class C {
|
||||||
|
var property by <caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// ABSENT: lazy
|
||||||
|
// EXIST: createX1
|
||||||
|
// ABSENT: createX2
|
||||||
|
// ABSENT: createX3
|
||||||
|
// EXIST: createX4
|
||||||
|
// ABSENT: createX5
|
||||||
+45
@@ -1436,6 +1436,51 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/idea-completion/testData/smart/propertyDelegate")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class PropertyDelegate extends AbstractJvmSmartCompletionTest {
|
||||||
|
public void testAllFilesPresentInPropertyDelegate() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/smart/propertyDelegate"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ExplicitValType.kt")
|
||||||
|
public void testExplicitValType() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ExplicitValType.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ExplicitVarType.kt")
|
||||||
|
public void testExplicitVarType() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ExplicitVarType.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ExtensionVal.kt")
|
||||||
|
public void testExtensionVal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ExtensionVal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ExtensionVar.kt")
|
||||||
|
public void testExtensionVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ExtensionVar.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ValInClass.kt")
|
||||||
|
public void testValInClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ValInClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("VarInClass.kt")
|
||||||
|
public void testVarInClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/VarInClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/idea-completion/testData/smart/smartCasts")
|
@TestMetadata("idea/idea-completion/testData/smart/smartCasts")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ class ExpectedInfos(
|
|||||||
?: calculateForReturn(expressionWithType)
|
?: calculateForReturn(expressionWithType)
|
||||||
?: calculateForLoopRange(expressionWithType)
|
?: calculateForLoopRange(expressionWithType)
|
||||||
?: calculateForInOperatorArgument(expressionWithType)
|
?: calculateForInOperatorArgument(expressionWithType)
|
||||||
|
?: calculateForPropertyDelegate(expressionWithType)
|
||||||
?: getFromBindingContext(expressionWithType)
|
?: getFromBindingContext(expressionWithType)
|
||||||
?: return emptyList()
|
?: return emptyList()
|
||||||
return expectedInfos.filterNot { it.fuzzyType?.type?.isError ?: false }
|
return expectedInfos.filterNot { it.fuzzyType?.type?.isError ?: false }
|
||||||
@@ -590,12 +591,44 @@ class ExpectedInfos(
|
|||||||
|
|
||||||
val byTypeFilter = object : ByTypeFilter {
|
val byTypeFilter = object : ByTypeFilter {
|
||||||
override fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? {
|
override fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? {
|
||||||
return if (detector.hasContains(descriptorType)) TypeSubstitutor.EMPTY else null
|
return if (detector.findOperator(descriptorType) != null) TypeSubstitutor.EMPTY else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return listOf(ExpectedInfo(byTypeFilter, null, null))
|
return listOf(ExpectedInfo(byTypeFilter, null, null))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun calculateForPropertyDelegate(expressionWithType: KtExpression): Collection<ExpectedInfo>? {
|
||||||
|
val delegate = expressionWithType.parent as? KtPropertyDelegate ?: return null
|
||||||
|
val propertyDeclaration = delegate.parent as? KtProperty ?: return null
|
||||||
|
val property = resolutionFacade.resolveToDescriptor(propertyDeclaration) as? PropertyDescriptor ?: return null
|
||||||
|
|
||||||
|
val scope = expressionWithType.getResolutionScope(bindingContext, resolutionFacade)
|
||||||
|
val propertyOwnerType = property.extensionReceiverParameter?.type
|
||||||
|
?: property.dispatchReceiverParameter?.type
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
val explicitPropertyType = property.returnType?.check { propertyDeclaration.typeReference != null }
|
||||||
|
val typesWithGetDetector = TypesWithGetValueDetector(scope, propertyOwnerType, explicitPropertyType)
|
||||||
|
val typesWithSetDetector = if (property.isVar) TypesWithSetValueDetector(scope, propertyOwnerType) else null
|
||||||
|
|
||||||
|
val byTypeFilter = object : ByTypeFilter {
|
||||||
|
override fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? {
|
||||||
|
val getValueOperator = 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
|
||||||
|
}
|
||||||
|
|
||||||
|
return TypeSubstitutor.EMPTY //TODO: substitutor from property type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listOf(ExpectedInfo(byTypeFilter, null, null))
|
||||||
|
//TODO: special items for "Delegates...."
|
||||||
|
}
|
||||||
|
|
||||||
private fun getFromBindingContext(expressionWithType: KtExpression): Collection<ExpectedInfo>? {
|
private fun getFromBindingContext(expressionWithType: KtExpression): Collection<ExpectedInfo>? {
|
||||||
val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null
|
val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null
|
||||||
return listOf(ExpectedInfo(expectedType, null, null))
|
return listOf(ExpectedInfo(expectedType, null, null))
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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.nullability
|
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
|
||||||
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.typeUtil.TypeNullability
|
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
|
||||||
import org.jetbrains.kotlin.util.isValidOperator
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
internal class TypesWithContainsDetector(
|
|
||||||
private val scope: LexicalScope,
|
|
||||||
private val argumentType: KotlinType
|
|
||||||
) {
|
|
||||||
private val cache = HashMap<FuzzyType, Boolean>()
|
|
||||||
|
|
||||||
private val typesWithExtensionContains: Collection<KotlinType> = scope
|
|
||||||
.collectFunctions(OperatorNameConventions.CONTAINS, NoLookupLocation.FROM_IDE)
|
|
||||||
.filter { it.extensionReceiverParameter != null && isGoodContainsFunction(it, listOf()) }
|
|
||||||
.map { it.extensionReceiverParameter!!.type }
|
|
||||||
|
|
||||||
fun hasContains(type: FuzzyType): Boolean {
|
|
||||||
return cache.getOrPut(type, { hasContainsNoCache(type) })
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hasContainsNoCache(type: FuzzyType): Boolean {
|
|
||||||
return type.nullability() != TypeNullability.NULLABLE &&
|
|
||||||
type.type.memberScope.getContributedFunctions(OperatorNameConventions.CONTAINS, NoLookupLocation.FROM_IDE).any { isGoodContainsFunction(it, type.freeParameters) }
|
|
||||||
|| typesWithExtensionContains.any { type.checkIsSubtypeOf(it) != null }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isGoodContainsFunction(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
|
|
||||||
if (!function.isValidOperator()) return false
|
|
||||||
val parameter = function.valueParameters.single()
|
|
||||||
val fuzzyParameterType = FuzzyType(parameter.type, function.typeParameters + freeTypeParams)
|
|
||||||
return fuzzyParameterType.checkIsSuperTypeOf(argumentType) != null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.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.typeUtil.TypeNullability
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
import org.jetbrains.kotlin.util.isValidOperator
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
//TODO: support not imported extensions
|
||||||
|
abstract class TypesWithOperatorDetector(
|
||||||
|
private val name: Name,
|
||||||
|
private val scope: LexicalScope
|
||||||
|
) {
|
||||||
|
protected abstract fun isSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean
|
||||||
|
|
||||||
|
private val cache = HashMap<FuzzyType, FunctionDescriptor?>()
|
||||||
|
|
||||||
|
private val typesWithExtension: 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findOperator(type: FuzzyType): FunctionDescriptor? {
|
||||||
|
if (cache.containsKey(type)) {
|
||||||
|
return cache[type]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val result = findOperatorNoCache(type)
|
||||||
|
cache[type] = result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findOperatorNoCache(type: FuzzyType): FunctionDescriptor? {
|
||||||
|
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 ((typeWithExtension, operator) in typesWithExtension) {
|
||||||
|
if (type.checkIsSubtypeOf(typeWithExtension) != null) {
|
||||||
|
return operator //TODO: substitution
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TypesWithContainsDetector(
|
||||||
|
scope: LexicalScope,
|
||||||
|
private val argumentType: KotlinType
|
||||||
|
) : TypesWithOperatorDetector(OperatorNameConventions.CONTAINS, scope) {
|
||||||
|
|
||||||
|
override fun isSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
|
||||||
|
val parameter = function.valueParameters.single()
|
||||||
|
val fuzzyParameterType = FuzzyType(parameter.type, function.typeParameters + freeTypeParams)
|
||||||
|
return fuzzyParameterType.checkIsSuperTypeOf(argumentType) != null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TypesWithGetValueDetector(
|
||||||
|
scope: LexicalScope,
|
||||||
|
private val propertyOwnerType: KotlinType,
|
||||||
|
private val propertyType: KotlinType?
|
||||||
|
) : TypesWithOperatorDetector(OperatorNameConventions.GET_VALUE, scope) {
|
||||||
|
|
||||||
|
override fun isSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
|
||||||
|
val paramType = FuzzyType(function.valueParameters.first().type, freeTypeParams)
|
||||||
|
if (paramType.checkIsSuperTypeOf(propertyOwnerType) == null) return false
|
||||||
|
|
||||||
|
if (propertyType != null) {
|
||||||
|
val returnType = FuzzyType(function.returnType ?: return false, freeTypeParams)
|
||||||
|
return returnType.checkIsSubtypeOf(propertyType) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TypesWithSetValueDetector(
|
||||||
|
scope: LexicalScope,
|
||||||
|
private val propertyOwnerType: KotlinType
|
||||||
|
) : TypesWithOperatorDetector(OperatorNameConventions.SET_VALUE, scope) {
|
||||||
|
|
||||||
|
override fun isSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
|
||||||
|
val paramType = FuzzyType(function.valueParameters.first().type, freeTypeParams)
|
||||||
|
return paramType.checkIsSuperTypeOf(propertyOwnerType) != null
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user