Synthetic properties: fixed completion and inspection for generic class

+ fixed KT-8539 No completion of generic extension function for <*> type arguments

 #KT-8539 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-07-17 19:57:01 +03:00
parent d2fb7381ce
commit e0e7044032
15 changed files with 103 additions and 31 deletions
@@ -30,15 +30,18 @@ import org.jetbrains.kotlin.resolve.lazy.FileScopeProvider
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.DescriptorSubstitutor
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.isBoolean
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeFirstWord
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
import org.jetbrains.kotlin.utils.addIfNotNull
import java.beans.Introspector
import java.util.*
import java.util.ArrayList
import java.util.HashSet
interface SyntheticJavaPropertyDescriptor : PropertyDescriptor {
val getMethod: FunctionDescriptor
@@ -54,9 +57,10 @@ interface SyntheticJavaPropertyDescriptor : PropertyDescriptor {
val owner = getterOrSetter.getContainingDeclaration()
if (owner !is JavaClassDescriptor) return null
val originalGetterOrSetter = getterOrSetter.original
return resolutionScope.getSyntheticExtensionProperties(listOf(owner.getDefaultType()))
.filterIsInstance<SyntheticJavaPropertyDescriptor>()
.firstOrNull { getterOrSetter == it.getMethod || getterOrSetter == it.setMethod }
.firstOrNull { originalGetterOrSetter == it.getMethod || originalGetterOrSetter == it.setMethod }
}
fun propertyNameByGetMethodName(methodName: Name): Name?
@@ -90,18 +94,18 @@ class AdditionalScopesWithJavaSyntheticExtensions(storageManager: StorageManager
}
class JavaSyntheticExtensionsScope(storageManager: StorageManager) : JetScope by JetScope.Empty {
private val syntheticPropertyInClass = storageManager.createMemoizedFunctionWithNullableValues<Triple<JavaClassDescriptor, JetType, Name>, PropertyDescriptor> { triple ->
syntheticPropertyInClassNotCached(triple.first, triple.second, triple.third)
private val syntheticPropertyInClass = storageManager.createMemoizedFunctionWithNullableValues<Pair<JavaClassDescriptor, Name>, PropertyDescriptor> { pair ->
syntheticPropertyInClassNotCached(pair.first, pair.second)
}
private fun syntheticPropertyInClassNotCached(javaClass: JavaClassDescriptor, type: JetType, name: Name): PropertyDescriptor? {
private fun syntheticPropertyInClassNotCached(javaClass: JavaClassDescriptor, name: Name): PropertyDescriptor? {
if (name.isSpecial()) return null
val identifier = name.identifier
if (identifier.isEmpty()) return null
val firstChar = identifier[0]
if (!firstChar.isJavaIdentifierStart() || firstChar.isUpperCase()) return null
val memberScope = javaClass.getMemberScope(type.getArguments())
val memberScope = javaClass.getUnsubstitutedMemberScope()
val getMethod = possibleGetMethodNames(name)
.asSequence()
.flatMap { memberScope.getFunctions(it).asSequence() }
@@ -113,7 +117,7 @@ class JavaSyntheticExtensionsScope(storageManager: StorageManager) : JetScope by
val propertyType = getMethod.getReturnType() ?: return null
val setMethod = memberScope.getFunctions(setMethodName(getMethod.getName())).singleOrNull { isGoodSetMethod(it, propertyType) }
return MyPropertyDescriptor(javaClass, getMethod, setMethod, name, propertyType, type)
return MyPropertyDescriptor(javaClass, getMethod.original, setMethod?.original, name, propertyType)
}
private fun isGoodGetMethod(descriptor: FunctionDescriptor): Boolean {
@@ -157,7 +161,7 @@ class JavaSyntheticExtensionsScope(storageManager: StorageManager) : JetScope by
val typeConstructor = type.getConstructor()
val classifier = typeConstructor.getDeclarationDescriptor()
if (classifier is JavaClassDescriptor) {
result = result.add(syntheticPropertyInClass(Triple(classifier, type, name)))
result = result.add(syntheticPropertyInClass(Pair(classifier, name)))
}
typeConstructor.getSupertypes().forEach { result = collectSyntheticPropertiesByName(result, it, name, processedTypes) }
@@ -180,10 +184,10 @@ class JavaSyntheticExtensionsScope(storageManager: StorageManager) : JetScope by
val typeConstructor = type.getConstructor()
val classifier = typeConstructor.getDeclarationDescriptor()
if (classifier is JavaClassDescriptor) {
for (descriptor in classifier.getMemberScope(type.getArguments()).getDescriptors(DescriptorKindFilter.FUNCTIONS)) {
for (descriptor in classifier.getUnsubstitutedMemberScope().getDescriptors(DescriptorKindFilter.FUNCTIONS)) {
if (descriptor is FunctionDescriptor) {
val propertyName = SyntheticJavaPropertyDescriptor.propertyNameByGetMethodName(descriptor.getName()) ?: continue
addIfNotNull(syntheticPropertyInClass(Triple(classifier, type, propertyName)))
addIfNotNull(syntheticPropertyInClass(Pair(classifier, propertyName)))
}
}
}
@@ -232,8 +236,7 @@ class JavaSyntheticExtensionsScope(storageManager: StorageManager) : JetScope by
override val getMethod: FunctionDescriptor,
override val setMethod: FunctionDescriptor?,
name: Name,
type: JetType,
receiverType: JetType
type: JetType
) : SyntheticJavaPropertyDescriptor, PropertyDescriptorImpl(
DescriptorUtils.getContainingModule(javaClass)/* TODO:is it ok? */,
null,
@@ -246,7 +249,13 @@ class JavaSyntheticExtensionsScope(storageManager: StorageManager) : JetScope by
SourceElement.NO_SOURCE/*TODO?*/
) {
init {
setType(type, emptyList(), null, receiverType)
val classTypeParams = javaClass.typeConstructor.parameters
val typeParameters = ArrayList<TypeParameterDescriptor>(classTypeParams.size())
val typeSubstitutor = DescriptorSubstitutor.substituteTypeParameters(classTypeParams, TypeSubstitutor.EMPTY, this, typeParameters)
val propertyType = typeSubstitutor.safeSubstitute(type, Variance.INVARIANT)
val receiverType = typeSubstitutor.safeSubstitute(javaClass.defaultType, Variance.INVARIANT)
setType(propertyType, typeParameters, null, receiverType)
val getter = PropertyGetterDescriptorImpl(this,
Annotations.EMPTY,
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.descriptors.impl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ReadOnly;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.name.Name;
@@ -82,7 +83,7 @@ public class PropertyDescriptorImpl extends VariableDescriptorImpl implements Pr
public void setType(
@NotNull JetType outType,
@NotNull List<? extends TypeParameterDescriptor> typeParameters,
@ReadOnly @NotNull List<? extends TypeParameterDescriptor> typeParameters,
@Nullable ReceiverParameterDescriptor dispatchReceiverParameter,
@Nullable JetType receiverType
) {
@@ -92,7 +93,7 @@ public class PropertyDescriptorImpl extends VariableDescriptorImpl implements Pr
public void setType(
@NotNull JetType outType,
@NotNull List<? extends TypeParameterDescriptor> typeParameters,
@ReadOnly @NotNull List<? extends TypeParameterDescriptor> typeParameters,
@Nullable ReceiverParameterDescriptor dispatchReceiverParameter,
@Nullable ReceiverParameterDescriptor extensionReceiverParameter
) {
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.types;
import org.jetbrains.annotations.Mutable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.ReadOnly;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.SourceElement;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
@@ -33,7 +34,7 @@ public class DescriptorSubstitutor {
@NotNull
public static TypeSubstitutor substituteTypeParameters(
@NotNull List<TypeParameterDescriptor> typeParameters,
@ReadOnly @NotNull List<TypeParameterDescriptor> typeParameters,
@NotNull final TypeSubstitutor originalSubstitutor,
@NotNull DeclarationDescriptor newContainingDeclaration,
@NotNull @Mutable List<TypeParameterDescriptor> result
@@ -75,13 +75,14 @@ public class ReferenceVariantsHelper(
if (filterOutJavaGettersAndSetters) {
val accessorMethodsToRemove = HashSet<FunctionDescriptor>()
for (variant in variants) {
if (variant is SyntheticJavaPropertyDescriptor) {
accessorMethodsToRemove.add(variant.getMethod)
accessorMethodsToRemove.addIfNotNull(variant.setMethod)
val original = variant.original
if (original is SyntheticJavaPropertyDescriptor) {
accessorMethodsToRemove.add(original.getMethod)
accessorMethodsToRemove.addIfNotNull(original.setMethod)
}
}
variants = variants.filter { it !in accessorMethodsToRemove }
variants = variants.filter { it.original !in accessorMethodsToRemove }
}
return variants
@@ -108,8 +108,8 @@ class FuzzyType(
constraintSystem.registerTypeVariables(freeParameters, { Variance.INVARIANT })
when (matchKind) {
MatchKind.IS_SUBTYPE -> constraintSystem.addSubtypeConstraint(type, otherType, ConstraintPositionKind.SPECIAL.position())
MatchKind.IS_SUPERTYPE -> constraintSystem.addSubtypeConstraint(otherType, type, ConstraintPositionKind.SPECIAL.position())
MatchKind.IS_SUBTYPE -> constraintSystem.addSubtypeConstraint(type, otherType, ConstraintPositionKind.RECEIVER_POSITION.position())
MatchKind.IS_SUPERTYPE -> constraintSystem.addSubtypeConstraint(otherType, type, ConstraintPositionKind.RECEIVER_POSITION.position())
}
constraintSystem.fixVariables()
@@ -33,16 +33,17 @@ import org.jetbrains.kotlin.utils.addIfNotNull
sealed class SyntheticPropertyAccessorReference(expression: JetNameReferenceExpression, private val getter: Boolean) : JetSimpleReference<JetNameReferenceExpression>(expression) {
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val descriptors = super.getTargetDescriptors(context)
if (descriptors.none { it is SyntheticJavaPropertyDescriptor }) return emptyList()
if (descriptors.none { it.original is SyntheticJavaPropertyDescriptor }) return emptyList()
val result = SmartList<FunctionDescriptor>()
for (descriptor in descriptors) {
if (descriptor is SyntheticJavaPropertyDescriptor) {
val original = descriptor.original
if (original is SyntheticJavaPropertyDescriptor) {
if (getter) {
result.add(descriptor.getMethod)
result.add(original.getMethod)
}
else {
result.addIfNotNull(descriptor.setMethod)
result.addIfNotNull(original.setMethod)
}
}
}
@@ -62,7 +63,7 @@ sealed class SyntheticPropertyAccessorReference(expression: JetNameReferenceExpr
}
else {
val propertyDescriptor = super.getTargetDescriptors(expression.analyze(BodyResolveMode.PARTIAL))
.singleOrNull { it is SyntheticJavaPropertyDescriptor } ?: return expression
.singleOrNull { it.original is SyntheticJavaPropertyDescriptor } ?: return expression
SyntheticJavaPropertyDescriptor.propertyNameBySetMethodName(newNameAsName, withIsPrefix = propertyDescriptor.getName().asString().startsWith("is"))
}
if (newName == null) return expression //TODO: handle the case when get/set becomes ordinary method
@@ -253,10 +253,11 @@ public class LookupElementFactory(
}
if (descriptor is CallableDescriptor) {
val original = descriptor.original
when {
descriptor is SyntheticJavaPropertyDescriptor -> {
var from = descriptor.getMethod.getName().asString() + "()"
descriptor.setMethod?.let { from += "/" + it.getName().asString() + "()" }
original is SyntheticJavaPropertyDescriptor -> {
var from = original.getMethod.getName().asString() + "()"
original.setMethod?.let { from += "/" + it.getName().asString() + "()" }
element = element.appendTailText(" (from $from)", true)
}
@@ -0,0 +1,9 @@
class MyClass<T>
fun <T> MyClass<T>.ext() = ""
fun foo(t: MyClass<*>) {
t.<caret>
}
// EXIST: ext
@@ -0,0 +1,10 @@
fun foo(klass: Class<*>) {
klass.<caret>
}
// EXIST_JAVA_ONLY: simpleName
// ABSENT: getSimpleName
// EXIST_JAVA_ONLY: enclosingClass
// ABSENT: getEnclosingClass
// EXIST_JAVA_ONLY: annotations
// ABSENT: getAnnotations
@@ -1233,6 +1233,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("StarTypeArg.kt")
public void testStarTypeArg() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/StarTypeArg.kt");
doTest(fileName);
}
@TestMetadata("SyntheticExtensions1.kt")
public void testSyntheticExtensions1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/SyntheticExtensions1.kt");
@@ -1245,6 +1251,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("SyntheticExtensionsInGenericClass.kt")
public void testSyntheticExtensionsInGenericClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/SyntheticExtensionsInGenericClass.kt");
doTest(fileName);
}
@TestMetadata("SyntheticExtensionsSafeCall.kt")
public void testSyntheticExtensionsSafeCall() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/SyntheticExtensionsSafeCall.kt");
@@ -1233,6 +1233,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("StarTypeArg.kt")
public void testStarTypeArg() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/StarTypeArg.kt");
doTest(fileName);
}
@TestMetadata("SyntheticExtensions1.kt")
public void testSyntheticExtensions1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/SyntheticExtensions1.kt");
@@ -1245,6 +1251,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("SyntheticExtensionsInGenericClass.kt")
public void testSyntheticExtensionsInGenericClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/SyntheticExtensionsInGenericClass.kt");
doTest(fileName);
}
@TestMetadata("SyntheticExtensionsSafeCall.kt")
public void testSyntheticExtensionsSafeCall() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/SyntheticExtensionsSafeCall.kt");
@@ -82,6 +82,7 @@ class UsePropertyAccessSyntaxIntention : JetSelfTargetingOffsetIndependentIntent
val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext, moduleDescriptor, callExpression.getProject(), ::isVisible)
val propertyName = property.getName()
val accessibleVariables = referenceVariantsHelper.getReferenceVariants(callee, DescriptorKindFilter.VARIABLES, { it == propertyName })
.map { it.original }
if (property !in accessibleVariables) return null // shadowed by something else
return property
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun foo(klass: Class<*>) {
klass.getEnclosingClass()<caret>
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun foo(klass: Class<*>) {
klass.enclosingClass<caret>
}
@@ -7415,6 +7415,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName);
}
@TestMetadata("genericClassMethod.kt")
public void testGenericClassMethod() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/usePropertyAccessSyntax/genericClassMethod.kt");
doTest(fileName);
}
@TestMetadata("get.kt")
public void testGet() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/usePropertyAccessSyntax/get.kt");