From 0399772ee65d24bdea93116e8fd6610c4d701ba1 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 11 Oct 2016 17:12:41 +0300 Subject: [PATCH] KT-9835 Completion thinks receiver is nullable when it is not #KT-9835 Fixed --- .../synthetic/JavaSyntheticPropertiesScope.kt | 7 +++++-- .../jetbrains/kotlin/idea/util/FuzzyType.kt | 20 +++++++++++++------ .../kotlin/idea/util/extensionsUtils.kt | 6 ++++-- .../basic/common/boldOrGrayed/KT9835.kt | 15 ++++++++++++++ .../syntheticExtensions/InGenericClass.kt | 5 +++++ .../basic/multifile/KT9835/KT9835.java | 4 ++++ .../testData/basic/multifile/KT9835/KT9835.kt | 8 ++++++++ .../test/JSBasicCompletionTestGenerated.java | 6 ++++++ .../test/JvmBasicCompletionTestGenerated.java | 6 ++++++ ...tiFileJvmBasicCompletionTestGenerated.java | 6 ++++++ 10 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 idea/idea-completion/testData/basic/common/boldOrGrayed/KT9835.kt create mode 100644 idea/idea-completion/testData/basic/multifile/KT9835/KT9835.java create mode 100644 idea/idea-completion/testData/basic/multifile/KT9835/KT9835.kt diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/JavaSyntheticPropertiesScope.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/JavaSyntheticPropertiesScope.kt index 7fd5de6c62f..9ecfee402a4 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/JavaSyntheticPropertiesScope.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/JavaSyntheticPropertiesScope.kt @@ -352,7 +352,7 @@ class JavaSyntheticPropertiesScope(storageManager: StorageManager, private val l } override fun substitute(originalSubstitutor: TypeSubstitutor): PropertyDescriptor? { - val descriptor = super.substitute(originalSubstitutor) as MyPropertyDescriptor + val descriptor = super.substitute(originalSubstitutor) as MyPropertyDescriptor? ?: return null if (descriptor == this) return descriptor val classTypeParameters = (getMethod.containingDeclaration as ClassDescriptor).typeConstructor.parameters @@ -362,7 +362,10 @@ class JavaSyntheticPropertiesScope(storageManager: StorageManager, private val l substitutionMap[classTypeParameter.typeConstructor] = typeProjection } - val classParametersSubstitutor = TypeSubstitutor.create(substitutionMap) + val classParametersSubstitutor = TypeConstructorSubstitution.createByConstructorsMap( + substitutionMap, + approximateCapturedTypes = true + ).buildSubstitutor() descriptor.getMethod = getMethod.substitute(classParametersSubstitutor) ?: return null descriptor.setMethod = setMethod?.substitute(classParametersSubstitutor) diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt index 7f1a89fbcac..2ed72813077 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.Constrain import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.StrictEqualityTypeChecker import org.jetbrains.kotlin.types.typeUtil.* +import org.jetbrains.kotlin.utils.addToStdlib.check import java.util.* fun CallableDescriptor.fuzzyReturnType() = returnType?.toFuzzyType(typeParameters) @@ -170,13 +171,20 @@ class FuzzyType( if (otherSubstitutedType.isError) return TypeSubstitutor.EMPTY if (!substitutedType.checkInheritance(otherSubstitutedType)) return null - val substitution = constraintSystem.typeVariables.map { it.originalTypeParameter }.associateBy({ it.typeConstructor }) { - val type = it.defaultType - val solution = substitutor.substitute(type, Variance.INVARIANT) - TypeProjectionImpl(if (solution != null && !ErrorUtils.containsUninferredParameter(solution)) solution else type) - } + val substitutorToKeepCapturedTypes = object : DelegatedTypeSubstitution(substitutor.substitution) { + override fun approximateCapturedTypes() = false + }.buildSubstitutor() - return TypeSubstitutor.create(TypeConstructorSubstitution.createByConstructorsMap(substitution)) + val substitutionMap: Map = constraintSystem.typeVariables + .map { it.originalTypeParameter } + .associateBy( + keySelector = { it.typeConstructor }, + valueTransform = { + val typeProjection = TypeProjectionImpl(Variance.INVARIANT, it.defaultType) + val substitutedProjection = substitutorToKeepCapturedTypes.substitute(typeProjection) + substitutedProjection?.check { !ErrorUtils.containsUninferredParameter(it.type) } ?: typeProjection + }) + return TypeConstructorSubstitution.createByConstructorsMap(substitutionMap, approximateCapturedTypes = true).buildSubstitutor() } } diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/extensionsUtils.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/extensionsUtils.kt index c68bda19946..97a7f4eed95 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/extensionsUtils.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/extensionsUtils.kt @@ -27,8 +27,8 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.TypeNullability import org.jetbrains.kotlin.types.typeUtil.makeNotNullable @@ -95,7 +95,9 @@ fun TCallable.substituteExtensionIfCallable( return if (substitutors.any()) listOf(this) else listOf() } else { - return substitutors.map { @Suppress("UNCHECKED_CAST") (substitute(it) as TCallable) }.toList() + return substitutors + .mapNotNull { @Suppress("UNCHECKED_CAST") (substitute(it) as TCallable?) } + .toList() } } diff --git a/idea/idea-completion/testData/basic/common/boldOrGrayed/KT9835.kt b/idea/idea-completion/testData/basic/common/boldOrGrayed/KT9835.kt new file mode 100644 index 00000000000..ef84f674871 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/boldOrGrayed/KT9835.kt @@ -0,0 +1,15 @@ +interface R + +val R.prop: Int get() = TODO() +fun R.extFun(): Int = TODO() + +interface I + +fun foo(r: R) { + r. +} + +// EXIST: { itemText: "prop", typeText: "Int", attributes: "bold" } +// EXIST: { itemText: "extFun", typeText: "Int", attributes: "bold" } + + diff --git a/idea/idea-completion/testData/basic/java/syntheticExtensions/InGenericClass.kt b/idea/idea-completion/testData/basic/java/syntheticExtensions/InGenericClass.kt index df5c4126380..f6c44ad9304 100644 --- a/idea/idea-completion/testData/basic/java/syntheticExtensions/InGenericClass.kt +++ b/idea/idea-completion/testData/basic/java/syntheticExtensions/InGenericClass.kt @@ -2,9 +2,14 @@ fun foo(klass: Class<*>) { klass. } +fun Class.extFun(): Class = TODO() + // EXIST: simpleName // ABSENT: getSimpleName // EXIST: enclosingClass // ABSENT: getEnclosingClass // EXIST: annotations // ABSENT: getAnnotations +// EXIST: superclass +// ABSENT: getSuperclass +// EXIST: extFun diff --git a/idea/idea-completion/testData/basic/multifile/KT9835/KT9835.java b/idea/idea-completion/testData/basic/multifile/KT9835/KT9835.java new file mode 100644 index 00000000000..d79ff65cbd2 --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/KT9835/KT9835.java @@ -0,0 +1,4 @@ +public interface R { + int getFoo(); + int f(); +} \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/multifile/KT9835/KT9835.kt b/idea/idea-completion/testData/basic/multifile/KT9835/KT9835.kt new file mode 100644 index 00000000000..9687724962b --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/KT9835/KT9835.kt @@ -0,0 +1,8 @@ +interface I + +fun foo(r: R) { + r. +} + +// EXIST: { itemText: "foo", tailText: " (from getFoo())", typeText: "Int", attributes: "bold" } +// EXIST: { itemText: "f", tailText: "()", typeText: "Int", attributes: "bold" } \ No newline at end of file diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java index 188dbd5ff18..28fb6f3dffb 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java @@ -1197,6 +1197,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes doTest(fileName); } + @TestMetadata("KT9835.kt") + public void testKT9835() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/boldOrGrayed/KT9835.kt"); + doTest(fileName); + } + @TestMetadata("NonPredictableSmartCast.kt") public void testNonPredictableSmartCast() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/boldOrGrayed/NonPredictableSmartCast.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java index 91e97e74467..23490f4f6dc 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java @@ -1197,6 +1197,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT doTest(fileName); } + @TestMetadata("KT9835.kt") + public void testKT9835() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/boldOrGrayed/KT9835.kt"); + doTest(fileName); + } + @TestMetadata("NonPredictableSmartCast.kt") public void testNonPredictableSmartCast() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/boldOrGrayed/NonPredictableSmartCast.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java index 4b5a72d3761..6cd7004bd23 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java @@ -209,6 +209,12 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ doTest(fileName); } + @TestMetadata("KT9835") + public void testKT9835() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/KT9835/"); + doTest(fileName); + } + @TestMetadata("MoreSpecificExtensionGeneric") public void testMoreSpecificExtensionGeneric() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/MoreSpecificExtensionGeneric/");