diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetDoubleColonExpression.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetDoubleColonExpression.kt index 859a195ac1b..80c70e028e9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetDoubleColonExpression.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetDoubleColonExpression.kt @@ -22,20 +22,22 @@ import org.jetbrains.kotlin.JetNodeTypes import org.jetbrains.kotlin.lexer.JetTokens public abstract class JetDoubleColonExpression(node: ASTNode) : JetExpressionImpl(node) { - public fun getTypeReference(): JetTypeReference? = findChildByType(JetNodeTypes.TYPE_REFERENCE) + public val typeReference: JetTypeReference? + get() = findChildByType(JetNodeTypes.TYPE_REFERENCE) + + public val doubleColonTokenReference: PsiElement + get() = findChildByType(JetTokens.COLONCOLON)!! public fun setTypeReference(typeReference: JetTypeReference) { - val oldTypeReference = getTypeReference() + val oldTypeReference = this.typeReference if (oldTypeReference != null) { oldTypeReference.replace(typeReference) } else { - addBefore(typeReference, getDoubleColonTokenReference()) + addBefore(typeReference, doubleColonTokenReference) } } - public fun getDoubleColonTokenReference(): PsiElement = findChildByType(JetTokens.COLONCOLON)!! - override fun accept(visitor: JetVisitor, data: D): R { return visitor.visitDoubleColonExpression(this, data) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt index 0ea4c15fc43..3b5f08b1850 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt @@ -59,7 +59,7 @@ public fun resolveCallableReferenceReceiverType( context: ResolutionContext<*>, typeResolver: TypeResolver ): JetType? = - callableReferenceExpression.getTypeReference()?.let { + callableReferenceExpression.typeReference?.let { typeResolver.resolveType(context.scope, it, context.trace, false) } diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt index 891a7f3436e..b3ed380b6e5 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt @@ -17,11 +17,7 @@ package org.jetbrains.kotlin.idea.codeInsight -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.frontendService import org.jetbrains.kotlin.idea.util.CallType @@ -38,12 +34,15 @@ import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression import org.jetbrains.kotlin.psi.psiUtil.isPackageDirectiveExpression import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotatedAsHidden import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension -import org.jetbrains.kotlin.resolve.scopes.* +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.JetScope +import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.utils.getDescriptorsFiltered import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.jetbrains.kotlin.types.JetType @@ -58,19 +57,10 @@ public class ReferenceVariantsHelper( private val visibilityFilter: (DeclarationDescriptor) -> Boolean ) { public data class ExplicitReceiverData( - val expression: JetExpression, + val element: JetElement?, val callType: CallType ) - public data class ReceiversData( - public val receivers: Collection, - public val callType: CallType - ) { - companion object { - val Empty = ReceiversData(listOf(), CallType.NORMAL) - } - } - @JvmOverloads public fun getReferenceVariants( expression: JetSimpleNameExpression, @@ -111,41 +101,48 @@ public class ReferenceVariantsHelper( if (expression.isImportDirectiveExpression()) { return getVariantsForImportOrPackageDirective(explicitReceiverData, kindFilter, nameFilter) } + if (expression.isPackageDirectiveExpression()) { val packageKindFilter = kindFilter restrictedToKinds DescriptorKindFilter.PACKAGES_MASK return getVariantsForImportOrPackageDirective(explicitReceiverData, packageKindFilter, nameFilter) } - if (expression.getParent() is JetUserType) { + + if (expression.parent is JetUserType) { return getVariantsForUserType(explicitReceiverData, expression, kindFilter, nameFilter) } - val resolutionScope = context[BindingContext.RESOLUTION_SCOPE, expression] ?: return listOf() + val resolutionScope = resolutionScope(expression) ?: return emptyList() + val dataFlowInfo = dataFlowInfo(expression) val containingDeclaration = resolutionScope.getContainingDeclaration() - val descriptors = LinkedHashSet() - - val dataFlowInfo = context.getDataFlowInfo(expression) - val smartCastManager = resolutionFacade.frontendService() val implicitReceiverTypes = resolutionScope.getImplicitReceiversWithInstance().flatMap { smartCastManager.getSmartCastVariantsWithLessSpecificExcluded(it.value, context, containingDeclaration, dataFlowInfo) }.toSet() - if (explicitReceiverData != null) { - val (receiverExpression, callType) = explicitReceiverData + val descriptors = LinkedHashSet() - val qualifier = context[BindingContext.QUALIFIER, receiverExpression] + if (explicitReceiverData != null) { + val (receiverElement, callType) = explicitReceiverData + + if (callType == CallType.CALLABLE_REFERENCE) { + return getVariantsForCallableReference(receiverElement as JetTypeReference?, resolutionScope, implicitReceiverTypes, kindFilter, nameFilter) + } + + receiverElement as JetExpression + + val qualifier = context[BindingContext.QUALIFIER, receiverElement] if (qualifier != null) { // It's impossible to add extension function for package or class (if it's companion object, expression type is not null) qualifier.scope.getDescriptorsFiltered(kindFilter exclude DescriptorKindExclude.Extensions, nameFilter).filterTo(descriptors) { callType.canCall(it) } } val expressionType = if (useRuntimeReceiverType) - getQualifierRuntimeType(receiverExpression) + getQualifierRuntimeType(receiverElement) else - context.getType(receiverExpression) + context.getType(receiverElement) if (expressionType != null && !expressionType.isError()) { - val receiverValue = ExpressionReceiver(receiverExpression, expressionType) + val receiverValue = ExpressionReceiver(receiverElement, expressionType) val explicitReceiverTypes = smartCastManager .getSmartCastVariantsWithLessSpecificExcluded(receiverValue, context, containingDeclaration, dataFlowInfo) @@ -155,12 +152,8 @@ public class ReferenceVariantsHelper( else { descriptors.processAll(implicitReceiverTypes, implicitReceiverTypes, resolutionScope, CallType.NORMAL, kindFilter, nameFilter) - // process non-instance members. - for (descriptor in resolutionScope.getDescriptorsFiltered(kindFilter, nameFilter)) { - if (!descriptor.isExtension) { - descriptors.add(descriptor) - } - } + // add non-instance members + descriptors.addAll(resolutionScope.getDescriptorsFiltered(kindFilter exclude DescriptorKindExclude.Extensions, nameFilter)) } return descriptors @@ -174,7 +167,8 @@ public class ReferenceVariantsHelper( ): Collection { val accurateKindFilter = kindFilter.restrictedToKinds(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK) if (explicitReceiverData != null) { - val qualifier = context[BindingContext.QUALIFIER, explicitReceiverData.expression] ?: return emptyList() + val receiverExpression = explicitReceiverData.element as? JetExpression ?: return emptyList() + val qualifier = context[BindingContext.QUALIFIER, receiverExpression] ?: return emptyList() return qualifier.scope.getDescriptorsFiltered(accurateKindFilter, nameFilter) } else { @@ -185,13 +179,42 @@ public class ReferenceVariantsHelper( } } + private fun getVariantsForCallableReference( + qualifierTypeRef: JetTypeReference?, + resolutionScope: JetScope, + implicitReceiverTypes: Collection, + kindFilter: DescriptorKindFilter, + nameFilter: (Name) -> Boolean + ): Collection { + val accurateKindFilter = kindFilter.restrictedToKinds(DescriptorKindFilter.CALLABLES.kindMask) + val descriptors = LinkedHashSet() + if (qualifierTypeRef != null) { + val type = context[BindingContext.TYPE, qualifierTypeRef] ?: return emptyList() + + descriptors.addNonExtensionMembers(listOf(type), CallType.CALLABLE_REFERENCE, accurateKindFilter, nameFilter, constructorsForInnerClassesOnly = false) + + descriptors.addScopeAndSyntheticExtensions(resolutionScope, listOf(type), CallType.CALLABLE_REFERENCE, accurateKindFilter, nameFilter) + } + else { + // process non-instance members and class constructors + descriptors.addNonExtensionCallablesAndConstructors(resolutionScope, CallType.CALLABLE_REFERENCE, kindFilter, nameFilter, constructorsForInnerClassesOnly = false) + + descriptors.addNonExtensionMembers(implicitReceiverTypes, CallType.CALLABLE_REFERENCE, accurateKindFilter, nameFilter, constructorsForInnerClassesOnly = true) + + //TODO: should we show synthetic extensions? get/set's? + descriptors.addScopeAndSyntheticExtensions(resolutionScope, implicitReceiverTypes, CallType.CALLABLE_REFERENCE, accurateKindFilter, nameFilter) + } + return descriptors + } + private fun getVariantsForImportOrPackageDirective( explicitReceiverData: ExplicitReceiverData?, kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean ): Collection { if (explicitReceiverData != null) { - val qualifier = context[BindingContext.QUALIFIER, explicitReceiverData.expression] ?: return emptyList() + val receiverExpression = explicitReceiverData.element as? JetExpression ?: return emptyList() + val qualifier = context[BindingContext.QUALIFIER, receiverExpression] ?: return emptyList() return qualifier.scope.getDescriptorsFiltered(kindFilter, nameFilter) } else { @@ -208,7 +231,7 @@ public class ReferenceVariantsHelper( kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean ) { - addNonExtensionMembers(receiverTypes, callType, kindFilter, nameFilter) + addNonExtensionMembers(receiverTypes, callType, kindFilter, nameFilter, constructorsForInnerClassesOnly = true) addMemberExtensions(implicitReceiverTypes, receiverTypes, callType, kindFilter, nameFilter) addScopeAndSyntheticExtensions(resolutionScope, receiverTypes, callType, kindFilter, nameFilter) } @@ -232,20 +255,36 @@ public class ReferenceVariantsHelper( receiverTypes: Collection, callType: CallType, kindFilter: DescriptorKindFilter, - nameFilter: (Name) -> Boolean + nameFilter: (Name) -> Boolean, + constructorsForInnerClassesOnly: Boolean ) { - val memberFilter = kindFilter exclude DescriptorKindExclude.Extensions for (receiverType in receiverTypes) { - val members = receiverType.memberScope.getDescriptorsFiltered(DescriptorKindFilter.ALL, nameFilter) // filter by kind later because of constructors - for (member in members) { - if (member is ClassDescriptor) { - if (member.isInner) { - member.constructors.filterTo(this) { callType.canCall(it) && memberFilter.accepts(it) } - } - } - else if (callType.canCall(member) && memberFilter.accepts(member)) { - this.add(member) - } + addNonExtensionCallablesAndConstructors(receiverType.memberScope, callType, kindFilter, nameFilter, constructorsForInnerClassesOnly) + } + } + + private fun MutableSet.addNonExtensionCallablesAndConstructors( + scope: JetScope, + callType: CallType, + kindFilter: DescriptorKindFilter, + nameFilter: (Name) -> Boolean, + constructorsForInnerClassesOnly: Boolean + ) { + var filterToUse = kindFilter.restrictedToKinds(DescriptorKindFilter.CALLABLES.kindMask).exclude(DescriptorKindExclude.Extensions) + + // should process classes if we need constructors + if (filterToUse.acceptsKinds(DescriptorKindFilter.FUNCTIONS_MASK)) { + filterToUse = filterToUse.withKinds(DescriptorKindFilter.NON_SINGLETON_CLASSIFIERS_MASK) + } + + for (descriptor in scope.getDescriptorsFiltered(filterToUse, nameFilter)) { + if (descriptor is ClassDescriptor) { + if (constructorsForInnerClassesOnly && !descriptor.isInner) continue + if (descriptor.modality == Modality.ABSTRACT || descriptor.modality == Modality.SEALED) continue + descriptor.constructors.filterTo(this) { callType.canCall(it) && kindFilter.accepts(it) } + } + else if (callType.canCall(descriptor) && kindFilter.accepts(descriptor)) { + this.add(descriptor) } } } @@ -259,7 +298,6 @@ public class ReferenceVariantsHelper( ) { if (kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) return - fun process(extension: CallableDescriptor) { if (nameFilter(extension.name) && kindFilter.accepts(extension)) { addAll(extension.substituteExtensionIfCallable(receiverTypes, callType)) @@ -286,19 +324,6 @@ public class ReferenceVariantsHelper( } } - public fun getReferenceVariantsReceivers(expression: JetSimpleNameExpression): ReceiversData { - val receiverData = getExplicitReceiverData(expression) - if (receiverData != null) { - val receiverExpression = receiverData.expression - val expressionType = context.getType(receiverExpression) ?: return ReceiversData.Empty - return ReceiversData(listOf(ExpressionReceiver(receiverExpression, expressionType)), receiverData.callType) - } - else { - val resolutionScope = context[BindingContext.RESOLUTION_SCOPE, expression] ?: return ReceiversData.Empty - return ReceiversData(resolutionScope.getImplicitReceiversWithInstance().map { it.getValue() }, CallType.NORMAL) - } - } - private fun getQualifierRuntimeType(receiver: JetExpression): JetType? { val type = context.getType(receiver) if (type != null && TypeUtils.canHaveSubtypes(JetTypeChecker.DEFAULT, type)) { @@ -316,10 +341,25 @@ public class ReferenceVariantsHelper( return resolutionScope.getDescriptorsFiltered(DescriptorKindFilter.PACKAGES, nameFilter).filter(visibilityFilter) } + //TODO: drop these methods + public fun resolutionScope(expression: JetSimpleNameExpression): JetScope? { + val parent = expression.parent + return context[BindingContext.RESOLUTION_SCOPE, if (parent is JetCallableReferenceExpression) parent else expression] + } + + public fun dataFlowInfo(expression: JetSimpleNameExpression): DataFlowInfo { + val parent = expression.parent + return context.getDataFlowInfo(if (parent is JetCallableReferenceExpression) parent else expression) + } + companion object { public fun getExplicitReceiverData(expression: JetSimpleNameExpression): ExplicitReceiverData? { + val parent = expression.parent + if (parent is JetCallableReferenceExpression) { + return ExplicitReceiverData(parent.typeReference, CallType.CALLABLE_REFERENCE) + } + val receiverExpression = expression.getReceiverExpression() ?: return null - val parent = expression.getParent() val callType = when (parent) { is JetBinaryExpression -> CallType.INFIX diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ShadowedDeclarationsFilter.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ShadowedDeclarationsFilter.kt index 638ccd1d5a9..13ad435de60 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ShadowedDeclarationsFilter.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ShadowedDeclarationsFilter.kt @@ -47,8 +47,9 @@ public class ShadowedDeclarationsFilter( private val dummyExpressionFactory = DummyExpressionFactory(psiFactory) private val explicitReceiverValue = explicitReceiverData?.let { - val type = bindingContext.getType(it.expression) ?: return@let null - ExpressionReceiver(it.expression, type) + val expression = it.element as? JetExpression ?: return@let null + val type = bindingContext.getType(expression) ?: return@let null + ExpressionReceiver(expression, type) } ?: ReceiverValue.NO_RECEIVER public fun filter(declarations: Collection): Collection { 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 4d74e2fa067..288312782ca 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 @@ -18,9 +18,7 @@ package org.jetbrains.kotlin.idea.util -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.psi.JetPsiUtil import org.jetbrains.kotlin.psi.JetThisExpression import org.jetbrains.kotlin.resolve.BindingContext @@ -47,7 +45,15 @@ public enum class CallType { UNARY { override fun canCall(descriptor: DeclarationDescriptor) = descriptor is SimpleFunctionDescriptor && descriptor.getValueParameters().size() == 0 - }; + }, + + CALLABLE_REFERENCE { + // currently callable references to locals and parameters are not supported + override fun canCall(descriptor: DeclarationDescriptor) + = descriptor is FunctionDescriptor || descriptor is PropertyDescriptor + } + + ; public open fun canCall(descriptor: DeclarationDescriptor): Boolean = true } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index d543e1e63c7..0a503d777ad 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -37,19 +37,21 @@ import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.resolve.frontendService import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.ShadowedDeclarationsFilter +import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstance import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance +import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList class CompletionSessionConfiguration( val completeNonImportedDeclarations: Boolean, @@ -123,45 +125,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC protected val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext, resolutionFacade, isVisibleFilter) - protected val receiversData: ReferenceVariantsHelper.ReceiversData? = nameExpression?.let { referenceVariantsHelper.getReferenceVariantsReceivers(it) } - - protected val lookupElementFactory = run { - val contextType = if (expression?.getParent() is JetSimpleNameStringTemplateEntry) - LookupElementFactory.ContextType.STRING_TEMPLATE_AFTER_DOLLAR - else if (receiversData?.callType == CallType.INFIX) - LookupElementFactory.ContextType.INFIX_CALL - else - LookupElementFactory.ContextType.NORMAL - - var receiverTypes = emptyList() - if (receiversData != null) { - val dataFlowInfo = bindingContext.getDataFlowInfo(expression) - - receiverTypes = receiversData.receivers.flatMap { receiverValue -> - val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverValue, bindingContext, moduleDescriptor) - if (dataFlowValue.isPredictable) { // we don't include smart cast receiver types for "unpredictable" receiver value to mark members grayed - resolutionFacade.frontendService() - .getSmartCastVariantsWithLessSpecificExcluded(receiverValue, bindingContext, moduleDescriptor, dataFlowInfo) - } - else { - listOf(receiverValue.type) - } - } - - if (receiversData.callType == CallType.SAFE) { - receiverTypes = receiverTypes.map { it.makeNotNullable() } - } - } - - val contextVariablesProvider = { - nameExpression?.let { - referenceVariantsHelper.getReferenceVariants(it, DescriptorKindFilter.VARIABLES, { true }, explicitReceiverData = null) - .map { it as VariableDescriptor } - } ?: emptyList() - } - - LookupElementFactory(resolutionFacade, receiverTypes, contextType, InsertHandlerProvider { expectedInfos }, contextVariablesProvider) - } + protected val lookupElementFactory = createLookupElementFactory() // LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes protected val collector: LookupElementsCollector by lazy(LazyThreadSafetyMode.NONE) { @@ -331,4 +295,65 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC { javaClass -> collector.addElement(lookupElementFactory.createLookupElementForJavaClass(javaClass), notImported = true) } ) } + + private fun createLookupElementFactory(): LookupElementFactory { + val (callType, receiverTypes) = detectCallTypeAndReceiverTypes() + + val contextVariablesProvider = { + nameExpression?.let { + referenceVariantsHelper.getReferenceVariants(it, DescriptorKindFilter.VARIABLES, { true }, explicitReceiverData = null) + .map { it as VariableDescriptor } + } ?: emptyList() + } + + val insertHandlerProvider = InsertHandlerProvider(callType) { expectedInfos } + return LookupElementFactory(resolutionFacade, receiverTypes, + callType, expression?.parent is JetSimpleNameStringTemplateEntry, + insertHandlerProvider, contextVariablesProvider) + } + + private fun detectCallTypeAndReceiverTypes(): Pair> { + if (nameExpression == null) { + return CallType.NORMAL to emptyList() + } + + val explicitReceiverData = ReferenceVariantsHelper.getExplicitReceiverData(nameExpression) + val receiverElement = explicitReceiverData?.element + val callType = explicitReceiverData?.callType ?: CallType.NORMAL + + if (explicitReceiverData != null && callType == CallType.CALLABLE_REFERENCE && receiverElement != null) { + val type = bindingContext[BindingContext.TYPE, receiverElement as JetTypeReference] + return callType to type.singletonOrEmptyList() + } + + receiverElement as JetExpression? + + val receiverValues = if (receiverElement != null) { + val expressionType = bindingContext.getType(receiverElement) + expressionType?.let { listOf(ExpressionReceiver(receiverElement, expressionType)) } ?: emptyList() + } + else { + val resolutionScope = referenceVariantsHelper.resolutionScope(nameExpression) + resolutionScope?.getImplicitReceiversWithInstance()?.map { it.value } ?: emptyList() + } + + val dataFlowInfo = referenceVariantsHelper.dataFlowInfo(nameExpression) + + var receiverTypes = receiverValues.flatMap { receiverValue -> + val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverValue, bindingContext, moduleDescriptor) + if (dataFlowValue.isPredictable) { // we don't include smart cast receiver types for "unpredictable" receiver value to mark members grayed + resolutionFacade.frontendService() + .getSmartCastVariantsWithLessSpecificExcluded(receiverValue, bindingContext, moduleDescriptor, dataFlowInfo) + } + else { + listOf(receiverValue.type) + } + } + + if (callType == CallType.SAFE) { + receiverTypes = receiverTypes.map { it.makeNotNullable() } + } + + return callType to receiverTypes + } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/InsertHandlerProvider.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/InsertHandlerProvider.kt index a8ad6cd533e..1bb68beed63 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/InsertHandlerProvider.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/InsertHandlerProvider.kt @@ -21,11 +21,15 @@ import com.intellij.codeInsight.lookup.LookupElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.completion.handlers.* +import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.fuzzyReturnType import org.jetbrains.kotlin.types.JetType import java.util.* -class InsertHandlerProvider(expectedInfosCalculator: () -> Collection) { +class InsertHandlerProvider( + private val callType: CallType, + expectedInfosCalculator: () -> Collection +) { private val expectedInfos by lazy(LazyThreadSafetyMode.NONE) { expectedInfosCalculator() } public fun insertHandler(descriptor: DeclarationDescriptor): InsertHandler { @@ -34,7 +38,7 @@ class InsertHandlerProvider(expectedInfosCalculator: () -> Collection KotlinFunctionInsertHandler(needTypeArguments, inputValueArguments = false) + 0 -> KotlinFunctionInsertHandler(callType, needTypeArguments, inputValueArguments = false) 1 -> { val parameterType = parameters.single().getType() @@ -42,13 +46,13 @@ class InsertHandlerProvider(expectedInfosCalculator: () -> Collection KotlinFunctionInsertHandler(needTypeArguments, inputValueArguments = true) + else -> KotlinFunctionInsertHandler(callType, needTypeArguments, inputValueArguments = true) } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KeywordCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KeywordCompletion.kt index f133df2e911..13b75391ad5 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KeywordCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KeywordCompletion.kt @@ -29,6 +29,7 @@ import com.intellij.psi.filters.position.PositionElementFilter import com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.KotlinKeywordInsertHandler +import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.lexer.JetKeywordToken import org.jetbrains.kotlin.lexer.JetTokens.* import org.jetbrains.kotlin.psi.* @@ -76,7 +77,7 @@ object KeywordCompletion { .withInsertHandler(if (keywordToken !in FUNCTION_KEYWORDS) KotlinKeywordInsertHandler else - KotlinFunctionInsertHandler(inputTypeArguments = false, inputValueArguments = false)) + KotlinFunctionInsertHandler(CallType.NORMAL, inputTypeArguments = false, inputValueArguments = false)) consumer(element) } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt index d2d57986a28..16a2ea21372 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.idea.completion.handlers.GenerateLambdaInfo import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.lambdaPresentation import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.FuzzyType import org.jetbrains.kotlin.idea.util.fuzzyReturnType import org.jetbrains.kotlin.name.FqName @@ -44,7 +45,8 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf class LookupElementFactory( private val resolutionFacade: ResolutionFacade, private val receiverTypes: Collection, - private val contextType: LookupElementFactory.ContextType, + private val callType: CallType, + private val isInStringTemplateAfterDollar: Boolean, public val insertHandlerProvider: InsertHandlerProvider, contextVariablesProvider: () -> Collection ) { @@ -54,23 +56,17 @@ class LookupElementFactory( contextVariablesProvider().filter { KotlinBuiltIns.isFunctionOrExtensionFunctionType(it.type) } } - public enum class ContextType { - NORMAL, - STRING_TEMPLATE_AFTER_DOLLAR, - INFIX_CALL - } - public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection { val result = SmartList() var lookupElement = createLookupElement(descriptor, useReceiverTypes) - if (contextType == ContextType.STRING_TEMPLATE_AFTER_DOLLAR && (descriptor is FunctionDescriptor || descriptor is ClassifierDescriptor)) { + if (isInStringTemplateAfterDollar && (descriptor is FunctionDescriptor || descriptor is ClassifierDescriptor)) { lookupElement = lookupElement.withBracesSurrounding() } result.add(lookupElement) // add special item for function with one argument of function type with more than one parameter - if (contextType != ContextType.INFIX_CALL && descriptor is FunctionDescriptor) { + if (descriptor is FunctionDescriptor && (callType == CallType.NORMAL || callType == CallType.SAFE)) { result.addSpecialFunctionCallElements(descriptor, useReceiverTypes) } @@ -136,11 +132,11 @@ class LookupElementFactory( } override fun handleInsert(context: InsertionContext) { - KotlinFunctionInsertHandler(inputTypeArguments, inputValueArguments = false, lambdaInfo = lambdaInfo).handleInsert(context, this) + KotlinFunctionInsertHandler(callType, inputTypeArguments, inputValueArguments = false, lambdaInfo = lambdaInfo).handleInsert(context, this) } } - if (contextType == ContextType.STRING_TEMPLATE_AFTER_DOLLAR) { + if (isInStringTemplateAfterDollar) { lookupElement = lookupElement.withBracesSurrounding() } @@ -153,7 +149,7 @@ class LookupElementFactory( val needTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler).inputTypeArguments lookupElement = FunctionCallWithArgumentLookupElement(lookupElement, descriptor, argumentText, needTypeArguments) - if (contextType == ContextType.STRING_TEMPLATE_AFTER_DOLLAR) { + if (isInStringTemplateAfterDollar) { lookupElement = lookupElement.withBracesSurrounding() } @@ -179,7 +175,7 @@ class LookupElementFactory( } override fun handleInsert(context: InsertionContext) { - KotlinFunctionInsertHandler(inputTypeArguments = needTypeArguments, inputValueArguments = false, argumentText = argumentText).handleInsert(context, this) + KotlinFunctionInsertHandler(callType, inputTypeArguments = needTypeArguments, inputValueArguments = false, argumentText = argumentText).handleInsert(context, this) } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PackageDirectiveCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PackageDirectiveCompletion.kt index 8c4e4154c81..2b0fcf4cb3f 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PackageDirectiveCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PackageDirectiveCompletion.kt @@ -23,6 +23,7 @@ import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.patterns.PlatformPatterns import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper +import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.JetPackageDirective import org.jetbrains.kotlin.psi.JetSimpleNameExpression @@ -53,7 +54,7 @@ object PackageDirectiveCompletion { val bindingContext = resolutionFacade.analyze(expression) val variants = ReferenceVariantsHelper(bindingContext, resolutionFacade, { true }).getPackageReferenceVariants(expression, prefixMatcher.asNameFilter()) - val lookupElementFactory = BasicLookupElementFactory(resolutionFacade.project, InsertHandlerProvider(expectedInfosCalculator = { emptyList() })) + val lookupElementFactory = BasicLookupElementFactory(resolutionFacade.project, InsertHandlerProvider(callType = CallType.NORMAL/*TODO*/, expectedInfosCalculator = { emptyList() })) for (variant in variants) { val lookupElement = lookupElementFactory.createLookupElement(variant) if (!lookupElement.getLookupString().contains(DUMMY_IDENTIFIER)) { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinFunctionInsertHandler.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinFunctionInsertHandler.kt index 323daa08eb5..e4e18ce0bdf 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinFunctionInsertHandler.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinFunctionInsertHandler.kt @@ -26,10 +26,9 @@ import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement import com.intellij.psi.codeStyle.CodeStyleSettingsManager import org.jetbrains.kotlin.idea.core.formatter.JetCodeStyleSettings +import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.lexer.JetTokens -import org.jetbrains.kotlin.psi.JetBinaryExpression import org.jetbrains.kotlin.psi.JetImportDirective -import org.jetbrains.kotlin.psi.JetSimpleNameExpression import org.jetbrains.kotlin.psi.JetTypeArgumentList import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType @@ -38,6 +37,7 @@ import org.jetbrains.kotlin.types.JetType class GenerateLambdaInfo(val lambdaType: JetType, val explicitParameters: Boolean) class KotlinFunctionInsertHandler( + val callType: CallType, val inputTypeArguments: Boolean, val inputValueArguments: Boolean, val argumentText: String = "", @@ -61,9 +61,10 @@ class KotlinFunctionInsertHandler( val element = context.getFile().findElementAt(startOffset) ?: return when { - element.getStrictParentOfType() != null -> return + //TODO: replace with CallType + element.getStrictParentOfType() != null || callType == CallType.CALLABLE_REFERENCE -> return - isInfixCall(element) -> { + callType == CallType.INFIX -> { if (context.getCompletionChar() == ' ') { context.setAddCompletionChar(false) } @@ -77,12 +78,6 @@ class KotlinFunctionInsertHandler( } } - private fun isInfixCall(context: PsiElement): Boolean { - val parent = context.getParent() - val grandParent = parent?.getParent() - return parent is JetSimpleNameExpression && grandParent is JetBinaryExpression && parent == grandParent.getOperationReference() - } - private fun addArguments(context : InsertionContext, offsetElement : PsiElement) { val completionChar = context.getCompletionChar() if (completionChar == '(') { //TODO: more correct behavior related to braces type diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt index dcaeadd334c..29b3c53b1a9 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptorKindExclude import org.jetbrains.kotlin.psi.FunctionLiteralArgument import org.jetbrains.kotlin.psi.JetCodeFragment +import org.jetbrains.kotlin.psi.JetExpression import org.jetbrains.kotlin.psi.ValueArgumentName import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall @@ -90,9 +91,9 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para // special completion for outside parenthesis lambda argument private fun addFunctionLiteralArgumentCompletions() { if (nameExpression != null) { - val (receiverExpression, callType) = ReferenceVariantsHelper.getExplicitReceiverData(nameExpression) ?: return + val (receiverElement, callType) = ReferenceVariantsHelper.getExplicitReceiverData(nameExpression) ?: return if (callType == CallType.INFIX) { - val call = receiverExpression.getCall(bindingContext) + val call = (receiverElement as JetExpression).getCall(bindingContext) if (call != null && call.getFunctionLiteralArguments().isEmpty()) { val dummyArgument = object : FunctionLiteralArgument { override fun getFunctionLiteral() = throw UnsupportedOperationException() diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt index 756a455e1fa..e4cb6ded4ec 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler import org.jetbrains.kotlin.idea.core.psiClassToDescriptor import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.FuzzyType import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.makeNotNullable @@ -203,9 +204,9 @@ class TypeInstantiationItems( } val baseInsertHandler = when (visibleConstructors.size()) { - 0 -> KotlinFunctionInsertHandler(inputTypeArguments = false, inputValueArguments = false) + 0 -> KotlinFunctionInsertHandler(CallType.NORMAL, inputTypeArguments = false, inputValueArguments = false) 1 -> lookupElementFactory.insertHandlerProvider.insertHandler(visibleConstructors.single()) as KotlinFunctionInsertHandler - else -> KotlinFunctionInsertHandler(inputTypeArguments = false, inputValueArguments = true) + else -> KotlinFunctionInsertHandler(CallType.NORMAL, inputTypeArguments = false, inputValueArguments = true) } insertHandler = object : InsertHandler { diff --git a/idea/idea-completion/testData/basic/common/callableReference/EmptyQualifier.kt b/idea/idea-completion/testData/basic/common/callableReference/EmptyQualifier.kt new file mode 100644 index 00000000000..aa18ee8cecc --- /dev/null +++ b/idea/idea-completion/testData/basic/common/callableReference/EmptyQualifier.kt @@ -0,0 +1,48 @@ +fun globalFun(p: Int) {} + +fun String.extensionFun(){} +val String.extensionVal: Int + get() = 1 + +val globalVal = 1 +var globalVar = 1 + +class C { + fun memberFun(){} + + val memberVal = 1 + + class NestedClass + inner class InnerClass + + fun foo() { + fun localFun(){} + + val local = 1 + + val v = :: + } + + companion object { + fun companionObjectFun(){} + } +} + +class WithPrivateConstructor private constructor() +abstract class AbstractClass + +// EXIST: { itemText: "globalFun", attributes: "" } +// EXIST: { itemText: "globalVal", attributes: "" } +// EXIST: { itemText: "globalVar", attributes: "" } +// ABSENT: extensionFun +// ABSENT: extensionVal +// EXIST: { itemText: "memberFun", attributes: "bold" } +// EXIST: { itemText: "memberVal", attributes: "bold" } +// EXIST: { itemText: "localFun", attributes: "" } +// ABSENT: { itemText: "local", attributes: "" } +// EXIST: { itemText: "companionObjectFun", attributes: "bold" } +// EXIST: { itemText: "C", attributes: "" } +// EXIST: { itemText: "NestedClass", attributes: "" } +// EXIST: { itemText: "InnerClass", attributes: "bold" } +// ABSENT: WithPrivateConstructor +// ABSENT: AbstractClass diff --git a/idea/idea-completion/testData/basic/common/callableReference/EmptyQualifierInExtensionFun.kt b/idea/idea-completion/testData/basic/common/callableReference/EmptyQualifierInExtensionFun.kt new file mode 100644 index 00000000000..3755a9f0c5d --- /dev/null +++ b/idea/idea-completion/testData/basic/common/callableReference/EmptyQualifierInExtensionFun.kt @@ -0,0 +1,23 @@ +class C { + fun memberFun(){} + + val memberVal = 1 + + class NestedClass + inner class InnerClass + + companion object { + fun companionObjectFun(){} + } +} + +fun C.foo() { + val v = :: +} + +// EXIST: { itemText: "memberFun", attributes: "bold" } +// EXIST: { itemText: "memberVal", attributes: "bold" } +// EXIST: { itemText: "hashCode", attributes: "" } +// ABSENT: companionObjectFun +// ABSENT: NestedClass +// EXIST: { itemText: "InnerClass", attributes: "bold" } diff --git a/idea/idea-completion/testData/basic/common/callableReference/HigherOrderFunction.kt b/idea/idea-completion/testData/basic/common/callableReference/HigherOrderFunction.kt new file mode 100644 index 00000000000..c09bf2a87fb --- /dev/null +++ b/idea/idea-completion/testData/basic/common/callableReference/HigherOrderFunction.kt @@ -0,0 +1,8 @@ +fun xfoo(p: (String, Char) -> Unit){} + +fun test() { + ::xfo +} + +// EXIST: { lookupString:"xfoo", itemText: "xfoo", tailText: "(p: (String, Char) -> Unit) ()", typeText:"Unit" } +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/basic/common/callableReference/NonEmptyQualifier.kt b/idea/idea-completion/testData/basic/common/callableReference/NonEmptyQualifier.kt new file mode 100644 index 00000000000..1649cfa09d7 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/callableReference/NonEmptyQualifier.kt @@ -0,0 +1,57 @@ +abstract class A { + abstract fun memberFunInA() + abstract val memberValInA: Int + + inner class InnerInA + class NestedInA +} + +fun A.extensionFun(){} + +val A.extensionVal: Int + get() = 1 + +fun Any.anyExtensionFun(){} +fun String.wrongExtensionFun(){} + +fun globalFun(p: Int) {} +val globalVal = 1 + +class C { + fun memberFun(){} + + val memberVal = 1 + + fun A.memberExtensionFun(){} + + fun foo() { + fun localFun(){} + + val v = A:: + } + + companion object { + fun companionObjectFun(){} + + fun A.companionExtension(){} + } +} + +/* TODO // EXIST: class */ +/* TODO // EXIST: class.java */ +// EXIST: { itemText: "memberFunInA", attributes: "bold" } +// EXIST: { itemText: "memberValInA", attributes: "bold" } +// EXIST: { itemText: "InnerInA", attributes: "bold" } +// EXIST: { itemText: "NestedInA", attributes: "" } +// EXIST: { itemText: "extensionFun", attributes: "bold" } +// EXIST: { itemText: "extensionVal", attributes: "bold" } +// EXIST: { itemText: "anyExtensionFun", attributes: "" } +// ABSENT: wrongExtensionFun +// ABSENT: globalFun +// ABSENT: globalVal +// ABSENT: memberFun +// ABSENT: memberVal +// ABSENT: memberExtensionFun +// ABSENT: localFun +// ABSENT: companionObjectFun +// ABSENT: companionExtension diff --git a/idea/idea-completion/testData/basic/common/callableReference/SmartCastThis.kt b/idea/idea-completion/testData/basic/common/callableReference/SmartCastThis.kt new file mode 100644 index 00000000000..f7960281b67 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/callableReference/SmartCastThis.kt @@ -0,0 +1,31 @@ +interface I1 { + fun i1Member() +} + +interface I2 { + fun i2Member() +} + +fun I1.i1Extension(){} +fun I2.i2Extension(){} +fun Any.anyExtension(){} +fun String.stringExtension(){} + +open class C { + fun cMember(){} + + fun foo() { + if (this is I1 && this is I2) { + val v = :: + } + } +} + +// EXIST: { itemText: "i1Extension", attributes: "bold" } +// EXIST: { itemText: "i2Extension", attributes: "bold" } +// EXIST: { itemText: "i1Member", attributes: "bold" } +// EXIST: { itemText: "i2Member", attributes: "bold" } +// EXIST: { itemText: "anyExtension", attributes: "" } +// EXIST: { itemText: "hashCode", attributes: "" } +// ABSENT: stringExtension +// EXIST: { itemText: "cMember", attributes: "bold" } diff --git a/idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions.kt b/idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions.kt new file mode 100644 index 00000000000..eb366abaf9f --- /dev/null +++ b/idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions.kt @@ -0,0 +1,7 @@ +import java.io.File + +val v = File:: + +// EXIST_JAVA_ONLY: { itemText: "freeSpace", tailText: " (from getFreeSpace())", attributes: "bold" } +// EXIST_JAVA_ONLY: { itemText: "isFile", tailText: " (from isFile())", attributes: "bold" } +// ABSENT: { itemText: "isFile", tailText: "()" } diff --git a/idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions2.kt b/idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions2.kt new file mode 100644 index 00000000000..aa3c741bb53 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions2.kt @@ -0,0 +1,9 @@ +import java.io.File + +class MyFile : File("") { + val v = :: +} + +// EXIST_JAVA_ONLY: { itemText: "freeSpace", tailText: " (from getFreeSpace())", attributes: "" } +// EXIST_JAVA_ONLY: { itemText: "isFile", tailText: " (from isFile())", attributes: "" } +// ABSENT: { itemText: "isFile", tailText: "()" } diff --git a/idea/idea-completion/testData/handlers/basic/callableReference/ClassConstructor.kt b/idea/idea-completion/testData/handlers/basic/callableReference/ClassConstructor.kt new file mode 100644 index 00000000000..3380161e199 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/callableReference/ClassConstructor.kt @@ -0,0 +1,11 @@ +package ppp + +class X(p: Int) + +class C { + fun foo() { + val v = :: + } +} + +// ELEMENT: X diff --git a/idea/idea-completion/testData/handlers/basic/callableReference/ClassConstructor.kt.after b/idea/idea-completion/testData/handlers/basic/callableReference/ClassConstructor.kt.after new file mode 100644 index 00000000000..6ca0de4b4ea --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/callableReference/ClassConstructor.kt.after @@ -0,0 +1,11 @@ +package ppp + +class X(p: Int) + +class C { + fun foo() { + val v = ::X + } +} + +// ELEMENT: X diff --git a/idea/idea-completion/testData/handlers/basic/callableReference/EmptyQualifier.kt b/idea/idea-completion/testData/handlers/basic/callableReference/EmptyQualifier.kt new file mode 100644 index 00000000000..92f30106c05 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/callableReference/EmptyQualifier.kt @@ -0,0 +1,9 @@ +fun globalFun(p: Int) {} + +class C { + fun foo() { + val v = :: + } +} + +// ELEMENT: globalFun diff --git a/idea/idea-completion/testData/handlers/basic/callableReference/EmptyQualifier.kt.after b/idea/idea-completion/testData/handlers/basic/callableReference/EmptyQualifier.kt.after new file mode 100644 index 00000000000..39cd106b427 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/callableReference/EmptyQualifier.kt.after @@ -0,0 +1,9 @@ +fun globalFun(p: Int) {} + +class C { + fun foo() { + val v = ::globalFun + } +} + +// ELEMENT: globalFun diff --git a/idea/idea-completion/testData/handlers/basic/callableReference/NonEmptyQualifier.kt b/idea/idea-completion/testData/handlers/basic/callableReference/NonEmptyQualifier.kt new file mode 100644 index 00000000000..1169d4959e7 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/callableReference/NonEmptyQualifier.kt @@ -0,0 +1,11 @@ +class C { + fun foo() { + val v = D:: + } +} + +class D { + fun memberFun(s: String){} +} + +// ELEMENT: memberFun diff --git a/idea/idea-completion/testData/handlers/basic/callableReference/NonEmptyQualifier.kt.after b/idea/idea-completion/testData/handlers/basic/callableReference/NonEmptyQualifier.kt.after new file mode 100644 index 00000000000..5188b836d5e --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/callableReference/NonEmptyQualifier.kt.after @@ -0,0 +1,11 @@ +class C { + fun foo() { + val v = D::memberFun + } +} + +class D { + fun memberFun(s: String){} +} + +// ELEMENT: memberFun diff --git a/idea/idea-completion/testData/handlers/basic/callableReference/Property.kt b/idea/idea-completion/testData/handlers/basic/callableReference/Property.kt new file mode 100644 index 00000000000..1b4e7560177 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/callableReference/Property.kt @@ -0,0 +1,9 @@ +class C { + val prop: Int = 0 + + fun foo() { + val v = :: + } +} + +// ELEMENT: prop diff --git a/idea/idea-completion/testData/handlers/basic/callableReference/Property.kt.after b/idea/idea-completion/testData/handlers/basic/callableReference/Property.kt.after new file mode 100644 index 00000000000..e3f4c71d3ff --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/callableReference/Property.kt.after @@ -0,0 +1,9 @@ +class C { + val prop: Int = 0 + + fun foo() { + val v = ::prop + } +} + +// ELEMENT: prop 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 007add0a025..60c5b5b8768 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 @@ -1066,6 +1066,57 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes } } + @TestMetadata("idea/idea-completion/testData/basic/common/callableReference") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CallableReference extends AbstractJSBasicCompletionTest { + public void testAllFilesPresentInCallableReference() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/callableReference"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("EmptyQualifier.kt") + public void testEmptyQualifier() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/EmptyQualifier.kt"); + doTest(fileName); + } + + @TestMetadata("EmptyQualifierInExtensionFun.kt") + public void testEmptyQualifierInExtensionFun() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/EmptyQualifierInExtensionFun.kt"); + doTest(fileName); + } + + @TestMetadata("HigherOrderFunction.kt") + public void testHigherOrderFunction() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/HigherOrderFunction.kt"); + doTest(fileName); + } + + @TestMetadata("NonEmptyQualifier.kt") + public void testNonEmptyQualifier() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/NonEmptyQualifier.kt"); + doTest(fileName); + } + + @TestMetadata("SmartCastThis.kt") + public void testSmartCastThis() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/SmartCastThis.kt"); + doTest(fileName); + } + + @TestMetadata("SyntheticExtensions.kt") + public void testSyntheticExtensions() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions.kt"); + doTest(fileName); + } + + @TestMetadata("SyntheticExtensions2.kt") + public void testSyntheticExtensions2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions2.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/idea-completion/testData/basic/common/extensions") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 ed883e673b0..d202ee6213e 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 @@ -1066,6 +1066,57 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT } } + @TestMetadata("idea/idea-completion/testData/basic/common/callableReference") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CallableReference extends AbstractJvmBasicCompletionTest { + public void testAllFilesPresentInCallableReference() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/callableReference"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("EmptyQualifier.kt") + public void testEmptyQualifier() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/EmptyQualifier.kt"); + doTest(fileName); + } + + @TestMetadata("EmptyQualifierInExtensionFun.kt") + public void testEmptyQualifierInExtensionFun() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/EmptyQualifierInExtensionFun.kt"); + doTest(fileName); + } + + @TestMetadata("HigherOrderFunction.kt") + public void testHigherOrderFunction() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/HigherOrderFunction.kt"); + doTest(fileName); + } + + @TestMetadata("NonEmptyQualifier.kt") + public void testNonEmptyQualifier() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/NonEmptyQualifier.kt"); + doTest(fileName); + } + + @TestMetadata("SmartCastThis.kt") + public void testSmartCastThis() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/SmartCastThis.kt"); + doTest(fileName); + } + + @TestMetadata("SyntheticExtensions.kt") + public void testSyntheticExtensions() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions.kt"); + doTest(fileName); + } + + @TestMetadata("SyntheticExtensions2.kt") + public void testSyntheticExtensions2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions2.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/idea-completion/testData/basic/common/extensions") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java index 8755fd183c2..aa7a7a93c92 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java @@ -155,6 +155,39 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion doTest(fileName); } + @TestMetadata("idea/idea-completion/testData/handlers/basic/callableReference") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CallableReference extends AbstractBasicCompletionHandlerTest { + public void testAllFilesPresentInCallableReference() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/handlers/basic/callableReference"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("ClassConstructor.kt") + public void testClassConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/callableReference/ClassConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("EmptyQualifier.kt") + public void testEmptyQualifier() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/callableReference/EmptyQualifier.kt"); + doTest(fileName); + } + + @TestMetadata("NonEmptyQualifier.kt") + public void testNonEmptyQualifier() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/callableReference/NonEmptyQualifier.kt"); + doTest(fileName); + } + + @TestMetadata("Property.kt") + public void testProperty() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/callableReference/Property.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/idea-completion/testData/handlers/basic/exclChar") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt index 5faeda676eb..798b9049c35 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt @@ -31,11 +31,9 @@ import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstance import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.JetCallableDeclaration -import org.jetbrains.kotlin.psi.JetFile -import org.jetbrains.kotlin.psi.JetNamedDeclaration -import org.jetbrains.kotlin.psi.JetSimpleNameExpression +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.bindingContextUtil.get import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager @@ -46,7 +44,6 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList -import org.jetbrains.kotlin.resolve.bindingContextUtil.get import java.util.* public class KotlinIndicesHelper( @@ -137,12 +134,15 @@ public class KotlinIndicesHelper( private fun receiverValues(expression: JetSimpleNameExpression, bindingContext: BindingContext): Collection> { val receiverData = ReferenceVariantsHelper.getExplicitReceiverData(expression) if (receiverData != null) { - val (receiverExpression, callType) = receiverData + val (receiverElement, callType) = receiverData - val expressionType = bindingContext.getType(receiverExpression) + if (callType == CallType.CALLABLE_REFERENCE) return emptyList() //TODO? + receiverElement as JetExpression + + val expressionType = bindingContext.getType(receiverElement) if (expressionType == null || expressionType.isError()) return emptyList() - val receiverValue = ExpressionReceiver(receiverExpression, expressionType) + val receiverValue = ExpressionReceiver(receiverElement, expressionType) return listOf(receiverValue to callType) }