diff options
Diffstat (limited to 'plugins/kotlin/fir/src')
19 files changed, 303 insertions, 145 deletions
diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirIconProvider.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirIconProvider.kt index 64383763fa21..c7f58156ada0 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirIconProvider.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirIconProvider.kt @@ -2,15 +2,23 @@ package org.jetbrains.kotlin.idea.completion +import com.intellij.psi.PsiElement import com.intellij.util.PlatformIcons import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.symbols.markers.* +import org.jetbrains.kotlin.psi.KtElement import javax.swing.Icon internal object KotlinFirIconProvider { fun getIconFor(symbol: KtSymbol): Icon? { + symbol.psi?.let { referencedPsi -> + if (referencedPsi !is KtElement) { + return getIconForJavaDeclaration(referencedPsi) + } + } + if (symbol is KtFunctionSymbol) { val isAbstract = symbol.modality == Modality.ABSTRACT @@ -52,4 +60,9 @@ internal object KotlinFirIconProvider { return null } + + private fun getIconForJavaDeclaration(declaration: PsiElement): Icon? { + val defaultIconFlags = 0 + return declaration.getIcon(defaultIconFlags) + } }
\ No newline at end of file diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCallableCompletionContributor.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCallableCompletionContributor.kt index f795bea614a4..518cffc2f143 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCallableCompletionContributor.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCallableCompletionContributor.kt @@ -161,7 +161,8 @@ internal open class FirCallableCompletionContributor( val symbol = explicitReceiver.reference()?.resolveToSymbol() when { symbol is KtPackageSymbol -> collectDotCompletionForPackageReceiver(symbol, context, visibilityChecker) - symbol is KtNamedClassOrObjectSymbol && symbol.classKind == KtClassKind.ENUM_CLASS -> { + + symbol is KtNamedClassOrObjectSymbol && symbol.hasImportantStaticMemberScope -> { collectNonExtensions(symbol.getStaticMemberScope(), visibilityChecker, scopeNameFilter).forEach { memberSymbol -> addCallableSymbolToCompletion( context, @@ -170,15 +171,24 @@ internal open class FirCallableCompletionContributor( ) } } - symbol is KtNamedClassOrObjectSymbol && !symbol.classKind.isObject && symbol.companionObject == null -> { + + symbol is KtNamedClassOrObjectSymbol && !symbol.canBeUsedAsReceiver -> { // symbol cannot be used as callable receiver } + else -> { collectDotCompletionForCallableReceiver(implicitScopes, explicitReceiver, context, extensionChecker, visibilityChecker) } } } + private val KtNamedClassOrObjectSymbol.hasImportantStaticMemberScope: Boolean + get() = classKind == KtClassKind.ENUM_CLASS || + origin == KtSymbolOrigin.JAVA + + private val KtNamedClassOrObjectSymbol.canBeUsedAsReceiver: Boolean + get() = classKind.isObject || companionObject != null + private fun KtAnalysisSession.collectDotCompletionForPackageReceiver( packageSymbol: KtPackageSymbol, context: WeighingContext, diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCompletionContributorBase.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCompletionContributorBase.kt index 0c5fa4fdae72..3cf5d82eca7a 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCompletionContributorBase.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCompletionContributorBase.kt @@ -132,32 +132,34 @@ internal abstract class FirCompletionContributorBase<C : FirRawPositionCompletio } } - // Make the text gray and insert type cast if the receiver type does not match. - is CallableMetadataProvider.CallableKind.ReceiverCastRequired -> object : LookupElementDecorator<LookupElement>(this) { - override fun renderElement(presentation: LookupElementPresentation) { - super.renderElement(presentation) - presentation.itemTextForeground = LookupElementFactory.CAST_REQUIRED_COLOR - // gray all tail fragments too: - val fragments = presentation.tailFragments - presentation.clearTail() - for (fragment in fragments) { - presentation.appendTailText(fragment.text, true) - } - } + // TODO this code should be uncommented when KTIJ-20913 is fixed + //// Make the text gray and insert type cast if the receiver type does not match. + //is CallableMetadataProvider.CallableKind.ReceiverCastRequired -> object : LookupElementDecorator<LookupElement>(this) { + // override fun renderElement(presentation: LookupElementPresentation) { + // super.renderElement(presentation) + // presentation.itemTextForeground = LookupElementFactory.CAST_REQUIRED_COLOR + // // gray all tail fragments too: + // val fragments = presentation.tailFragments + // presentation.clearTail() + // for (fragment in fragments) { + // presentation.appendTailText(fragment.text, true) + // } + // } + // + // override fun handleInsert(context: InsertionContext) { + // super.handleInsert(context) + // if (explicitReceiverRange == null || explicitReceiverText == null) return + // val castType = explicitReceiverTypeHint ?: kind.fullyQualifiedCastType + // val newReceiver = "(${explicitReceiverText} as $castType)" + // context.document.replaceString(explicitReceiverRange.startOffset, explicitReceiverRange.endOffset, newReceiver) + // context.commitDocument() + // shortenReferencesInRange( + // context.file as KtFile, + // explicitReceiverRange.grown(newReceiver.length) + // ) + // } + //} - override fun handleInsert(context: InsertionContext) { - super.handleInsert(context) - if (explicitReceiverRange == null || explicitReceiverText == null) return - val castType = explicitReceiverTypeHint ?: kind.fullyQualifiedCastType - val newReceiver = "(${explicitReceiverText} as $castType)" - context.document.replaceString(explicitReceiverRange.startOffset, explicitReceiverRange.endOffset, newReceiver) - context.commitDocument() - shortenReferencesInRange( - context.file as KtFile, - explicitReceiverRange.grown(newReceiver.length) - ) - } - } else -> this } } diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesSupportFirImpl.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesSupportFirImpl.kt index a412d09b2aec..d35b732402e0 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesSupportFirImpl.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesSupportFirImpl.kt @@ -12,11 +12,7 @@ import com.intellij.psi.search.GlobalSearchScope import com.intellij.util.Processor import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.analyseInModalWindow -import org.jetbrains.kotlin.analysis.api.analyseWithReadAction -import org.jetbrains.kotlin.analysis.api.calls.KtCall -import org.jetbrains.kotlin.analysis.api.calls.KtCallableMemberCall -import org.jetbrains.kotlin.analysis.api.calls.KtImplicitReceiverValue -import org.jetbrains.kotlin.analysis.api.calls.calls +import org.jetbrains.kotlin.analysis.api.calls.* import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithKind @@ -25,11 +21,13 @@ import org.jetbrains.kotlin.idea.core.util.showYesNoCancelDialog import org.jetbrains.kotlin.idea.refactoring.CHECK_SUPER_METHODS_YES_NO_DIALOG import org.jetbrains.kotlin.idea.refactoring.formatPsiClass import org.jetbrains.kotlin.idea.references.KtInvokeFunctionReference +import org.jetbrains.kotlin.idea.util.withResolvedCall import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType class KotlinFindUsagesSupportFirImpl : KotlinFindUsagesSupport { @@ -40,10 +38,9 @@ class KotlinFindUsagesSupportFirImpl : KotlinFindUsagesSupport { val klass = companionObject.getStrictParentOfType<KtClass>() ?: return true return !klass.anyDescendantOfType(fun(element: KtElement): Boolean { if (element == companionObject) return false - var result = false - forResolvedCall(element) { call -> + return withResolvedCall(element) { call -> if (callReceiverRefersToCompanionObject(call, companionObject)) { - result = element.references.any { + element.references.any { // We get both a simple named reference and an invoke function // reference for all function calls. We want the named reference. // @@ -53,18 +50,13 @@ class KotlinFindUsagesSupportFirImpl : KotlinFindUsagesSupport { // We should make FIR references behave the same. it !is KtInvokeFunctionReference && !referenceProcessor.process(it) } + } else { + false } - } - return result + } ?: false }) } - private fun forResolvedCall(element: KtElement, block: KtAnalysisSession.(KtCall) -> Unit) { - analyseWithReadAction(element) { - element.resolveCall()?.calls?.singleOrNull()?.let { block(it) } - } - } - private fun KtAnalysisSession.callReceiverRefersToCompanionObject(call: KtCall, companionObject: KtObjectDeclaration): Boolean { if (call !is KtCallableMemberCall<*, *>) return false val dispatchReceiver = call.partiallyAppliedSymbol.dispatchReceiver @@ -89,9 +81,20 @@ class KotlinFindUsagesSupportFirImpl : KotlinFindUsagesSupport { return (declaration as? KtNamedDeclaration)?.name ?: "SUPPORT FOR FIR" } - override fun isConstructorUsage(psiReference: PsiReference, ktClassOrObject: KtClassOrObject): Boolean { - // TODO: implement this - return false + override fun isKotlinConstructorUsage(psiReference: PsiReference, ktClassOrObject: KtClassOrObject): Boolean { + val element = psiReference.element + if (element !is KtElement) return false + + val constructorCalleeExpression = element.getNonStrictParentOfType<KtConstructorCalleeExpression>() ?: return false + return withResolvedCall(constructorCalleeExpression) { call -> + when (call) { + is KtDelegatedConstructorCall -> { + val constructedClassSymbol = call.symbol.containingClassIdIfNonLocal?.getCorrespondingToplevelClassOrObjectSymbol() + constructedClassSymbol == ktClassOrObject.getClassOrObjectSymbol() + } + else -> false + } + } ?: false } override fun getSuperMethods(declaration: KtDeclaration, ignore: Collection<PsiElement>?): List<PsiElement> { diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/HiglightingFactory.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/HiglightingFactory.kt new file mode 100644 index 000000000000..5f15fb41d8e6 --- /dev/null +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/HiglightingFactory.kt @@ -0,0 +1,22 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.kotlin.idea.fir.highlighter + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.HighlightSeverity +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.util.TextRange +import com.intellij.util.applyIf + +internal object HiglightingFactory { + fun createInfoAnnotation(holder: AnnotationHolder, textRange: TextRange, message: String?, textAttributes: TextAttributesKey?) { + val builder = + if (message == null) holder.newSilentAnnotation(HighlightSeverity.INFORMATION) + else holder.newAnnotation(HighlightSeverity.INFORMATION, message) + builder + .range(textRange) + .applyIf(textAttributes != null) { + textAttributes(textAttributes!!) + } + .create() + } +}
\ No newline at end of file diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/KotlinHighLevelDiagnosticHighlightingPass.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/KotlinHighLevelDiagnosticHighlightingPass.kt index aa9b17b3b6da..a75759b77c01 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/KotlinHighLevelDiagnosticHighlightingPass.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/KotlinHighLevelDiagnosticHighlightingPass.kt @@ -37,7 +37,7 @@ class KotlinHighLevelDiagnosticHighlightingPass( ) : TextEditorHighlightingPass(ktFile.project, document) { @Suppress("UnstableApiUsage") - val annotationHolder = AnnotationHolderImpl(AnnotationSession(ktFile)) + val annotationHolder = AnnotationHolderImpl(AnnotationSession(ktFile), false) override fun doCollectInformation(progress: ProgressIndicator) { analyse(ktFile) { diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/beforeResolve/AbstractBeforeResolveHiglightingVisitory.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/beforeResolve/AbstractBeforeResolveHiglightingVisitory.kt new file mode 100644 index 000000000000..e1215b2c51b1 --- /dev/null +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/beforeResolve/AbstractBeforeResolveHiglightingVisitory.kt @@ -0,0 +1,16 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.kotlin.idea.fir.highlighter.beforeResolve + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.HighlightSeverity +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.util.TextRange +import com.intellij.util.applyIf +import org.jetbrains.kotlin.idea.fir.highlighter.HiglightingFactory +import org.jetbrains.kotlin.idea.highlighter.AbstractHighlightingVisitor + +abstract class AbstractBeforeResolveHiglightingVisitory(protected val holder: AnnotationHolder): AbstractHighlightingVisitor() { + override fun createInfoAnnotation(textRange: TextRange, message: String?, textAttributes: TextAttributesKey?) { + HiglightingFactory.createInfoAnnotation(holder, textRange, message, textAttributes) + } +}
\ No newline at end of file diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/beforeResolve/AnnotationEntryHighlightingVisitor.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/beforeResolve/AnnotationEntryHighlightingVisitor.kt new file mode 100644 index 000000000000..0fa128b53c8f --- /dev/null +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/beforeResolve/AnnotationEntryHighlightingVisitor.kt @@ -0,0 +1,35 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + +package org.jetbrains.kotlin.idea.fir.highlighter.beforeResolve + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.openapi.util.TextRange +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.idea.highlighter.AbstractHighlightingVisitor +import org.jetbrains.kotlin.idea.highlighter.BeforeResolveHighlightingExtension +import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors +import org.jetbrains.kotlin.idea.highlighter.NameHighlighter +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffset + +internal class AnnotationEntryHighlightingVisitor( + holder: AnnotationHolder +) : AbstractBeforeResolveHiglightingVisitory(holder) { + override fun visitAnnotationEntry(annotationEntry: KtAnnotationEntry) { + if (!NameHighlighter.namesHighlightingEnabled) return + val range = annotationEntry.getTextRangeToHiglight() ?: return + createInfoAnnotation(range, textAttributes = KotlinHighlightingColors.ANNOTATION) + } + + private fun KtAnnotationEntry.getTextRangeToHiglight(): TextRange? { + val atSymbol = atSymbol ?: return null + val typeReference = typeReference ?: return null + return TextRange(atSymbol.startOffset, typeReference.endOffset) + } +} + +class AnnotationsHighlightingExtension : BeforeResolveHighlightingExtension { + override fun createVisitor(holder: AnnotationHolder): AbstractHighlightingVisitor = + AnnotationEntryHighlightingVisitor(holder) +}
\ No newline at end of file diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/DeclarationHighlightingVisitor.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/beforeResolve/DeclarationHighlightingVisitor.kt index d28c062c6474..e25006a87395 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/DeclarationHighlightingVisitor.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/beforeResolve/DeclarationHighlightingVisitor.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package org.jetbrains.kotlin.idea.fir.highlighter.visitors +package org.jetbrains.kotlin.idea.fir.highlighter.beforeResolve import com.intellij.lang.annotation.AnnotationHolder import com.intellij.lang.annotation.HighlightSeverity @@ -16,7 +16,7 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors as Colors -internal class DeclarationHighlightingVisitor(private val holder: AnnotationHolder) : AbstractHighlightingVisitor() { +internal class DeclarationHighlightingVisitor(holder: AnnotationHolder) : AbstractBeforeResolveHiglightingVisitory(holder) { override fun visitTypeAlias(typeAlias: KtTypeAlias) { highlightNamedDeclaration(typeAlias, Colors.TYPE_ALIAS) super.visitTypeAlias(typeAlias) @@ -27,22 +27,6 @@ internal class DeclarationHighlightingVisitor(private val holder: AnnotationHold super.visitObjectDeclaration(declaration) } - override fun createInfoAnnotation( - textRange: com.intellij.openapi.util.TextRange, - message: kotlin.String?, - textAttributes: com.intellij.openapi.editor.colors.TextAttributesKey? - ) { - return (message?.let { holder.newAnnotation(HighlightSeverity.INFORMATION, it) } - ?: holder.newSilentAnnotation(HighlightSeverity.INFORMATION)) - .range(textRange) - .also { builder -> - textAttributes?.let { - builder.textAttributes(it) - } - } - .create() - } - override fun visitClass(klass: KtClass) { highlightNamedDeclaration(klass, textAttributesForClass(klass)) super.visitClass(klass) diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/FirAfterResolveHighlightingVisitor.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/FirAfterResolveHighlightingVisitor.kt index b7d71d9d4720..ff64768728bb 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/FirAfterResolveHighlightingVisitor.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/FirAfterResolveHighlightingVisitor.kt @@ -3,9 +3,12 @@ package org.jetbrains.kotlin.idea.fir.highlighter.visitors import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.HighlightSeverity import com.intellij.openapi.editor.colors.TextAttributesKey import com.intellij.openapi.util.TextRange +import com.intellij.util.applyIf import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.idea.fir.highlighter.HiglightingFactory import org.jetbrains.kotlin.idea.highlighter.AbstractHighlightingVisitor abstract class FirAfterResolveHighlightingVisitor( @@ -14,9 +17,7 @@ abstract class FirAfterResolveHighlightingVisitor( ) : AbstractHighlightingVisitor() { override fun createInfoAnnotation(textRange: TextRange, message: String?, textAttributes: TextAttributesKey?) { - // TODO: Temporary use deprecated for FIR plugin as it is supposes to be rewritten fully - holder.createInfoAnnotation(textRange, message) - .also { annotation -> textAttributes?.let { annotation.textAttributes = textAttributes } } + HiglightingFactory.createInfoAnnotation(holder, textRange, message, textAttributes) } companion object { diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/TypeHighlightingVisitor.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/TypeHighlightingVisitor.kt index bce0c124f88e..2726ea372076 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/TypeHighlightingVisitor.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/TypeHighlightingVisitor.kt @@ -28,29 +28,29 @@ internal class TypeHighlightingVisitor( return } val target = expression.mainReference.resolve() ?: return + if (isAnnotationCall(expression, target)) { + // higlighted by AnnotationEntryHiglightingVisitor + return + } textAttributesKeyForTypeDeclaration(target)?.let { key -> if (expression.isConstructorCallReference() && key != Colors.ANNOTATION) { // Do not highlight constructor call as class reference return@let } - highlightName(computeHighlightingRangeForUsage(expression, target), key) + highlightName(expression.textRange, key) } } - - private fun computeHighlightingRangeForUsage(expression: KtSimpleNameExpression, target: PsiElement): TextRange { + private fun isAnnotationCall(expression: KtSimpleNameExpression, target: PsiElement): Boolean { val expressionRange = expression.textRange val isKotlinAnnotation = target is KtPrimaryConstructor && target.parent.isAnnotationClass() - if (!isKotlinAnnotation && !target.isAnnotationClass()) return expressionRange + if (!isKotlinAnnotation && !target.isAnnotationClass()) return false - // include '@' symbol if the reference is the first segment of KtAnnotationEntry - // if "Deprecated" is highlighted then '@' should be highlighted too in "@Deprecated" val annotationEntry = PsiTreeUtil.getParentOfType( expression, KtAnnotationEntry::class.java, /* strict = */false, KtValueArgumentList::class.java ) - val atSymbol = annotationEntry?.atSymbol ?: return expressionRange - return TextRange(atSymbol.textRange.startOffset, expression.textRange.endOffset) + return annotationEntry?.atSymbol != null } } diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/VariableReferenceHighlightingVisitor.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/VariableReferenceHighlightingVisitor.kt index 08d1776f9a5d..81aeb2abcf0e 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/VariableReferenceHighlightingVisitor.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/VariableReferenceHighlightingVisitor.kt @@ -3,21 +3,20 @@ package org.jetbrains.kotlin.idea.fir.highlighter.visitors import com.intellij.lang.annotation.AnnotationHolder -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMethod -import com.intellij.psi.PsiModifier -import com.intellij.psi.PsiVariable -import com.intellij.psi.util.PsiUtilCore -import com.intellij.psi.util.parentOfType -import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle -import org.jetbrains.kotlin.idea.highlighter.textAttributesKeyForPropertyDeclaration import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.symbols.KtBackingFieldSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtSyntheticJavaPropertySymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtVariableSymbol +import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle import org.jetbrains.kotlin.idea.highlighter.NameHighlighter +import org.jetbrains.kotlin.idea.highlighter.textAttributesKeyForPropertyDeclaration import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.KtInstanceExpressionWithLabel +import org.jetbrains.kotlin.psi.KtOperationReferenceExpression +import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.KtValueArgumentName import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors as Colors internal class VariableReferenceHighlightingVisitor( @@ -41,15 +40,17 @@ internal class VariableReferenceHighlightingVisitor( with(analysisSession) { val targetSymbol = expression.mainReference.resolveToSymbol() - val target = expression.mainReference.resolve() + val targetPsi = targetSymbol?.psi when { targetSymbol is KtBackingFieldSymbol -> Colors.BACKING_FIELD_VARIABLE - target is PsiMethod -> Colors.SYNTHETIC_EXTENSION_PROPERTY - target != null -> textAttributesKeyForPropertyDeclaration(target) + targetSymbol is KtSyntheticJavaPropertySymbol -> Colors.SYNTHETIC_EXTENSION_PROPERTY + targetPsi != null -> textAttributesKeyForPropertyDeclaration(targetPsi) else -> null }?.let { attribute -> highlightName(expression, attribute) - if (target?.isMutableVariable() == true || targetSymbol != null && isBackingFieldReferencingMutableVariable(targetSymbol)) { + if (isMutableVariable(targetSymbol) == true + || targetSymbol != null && isBackingFieldReferencingMutableVariable(targetSymbol) + ) { highlightName(expression, Colors.MUTABLE_VARIABLE) } } @@ -71,9 +72,9 @@ internal class VariableReferenceHighlightingVisitor( } } -private fun PsiElement.isMutableVariable() = when { - this is KtValVarKeywordOwner && PsiUtilCore.getElementType(valOrVarKeyword) == KtTokens.VAR_KEYWORD -> true - this is PsiVariable && !hasModifierProperty(PsiModifier.FINAL) -> true +@Suppress("unused") +private fun KtAnalysisSession.isMutableVariable(symbol: KtSymbol?): Boolean = when (symbol) { + is KtVariableSymbol -> !symbol.isVal else -> false } diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinHighLevelFunctionParameterInfoHandler.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinHighLevelFunctionParameterInfoHandler.kt index c5203435ecdf..bdd68250f040 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinHighLevelFunctionParameterInfoHandler.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinHighLevelFunctionParameterInfoHandler.kt @@ -12,6 +12,7 @@ import com.intellij.ui.JBColor import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.analyse import org.jetbrains.kotlin.analysis.api.annotations.annotations +import org.jetbrains.kotlin.analysis.api.calls.KtApplicableCallCandidateInfo import org.jetbrains.kotlin.analysis.api.components.KtTypeRendererOptions import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtVariableLikeSignature @@ -115,7 +116,9 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K // `context.itemsToShow` array which becomes `context.objectsToView` array in updateParameterInfo(). Unfortunately // `objectsToView` is read-only so we can't change the size of the array. So we have to store an array here of the correct size, // which does mean we have to resolve here to know the number of candidates. - val candidatesWithMapping = resolveCallCandidates(callElement) + val candidatesWithMapping = collectCallCandidates(callElement) + + // TODO: Filter shadowed candidates. See use of ShadowedDeclarationsFilter in KotlinFunctionParameterInfoHandler.kt. context.itemsToShow = Array(candidatesWithMapping.size) { CandidateInfo() } argumentList @@ -145,7 +148,8 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K is KtArrayAccessExpression -> Pair(null, callElement.indexExpressions) else -> return@analyse } - val candidatesWithMapping = resolveCallCandidates(callElement) + val candidatesWithMapping = collectCallCandidates(callElement) + val hasMultipleApplicableBestCandidates = candidatesWithMapping.count { it.isApplicableBestCandidate } > 1 for ((index, objectToView) in context.objectsToView.withIndex()) { val candidateInfo = objectToView as? CandidateInfo ?: continue @@ -154,7 +158,7 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K // Number of candidates somehow changed while UI is shown, which should NOT be possible. Bail out to be safe. return } - val (candidateSignature, argumentMapping) = candidatesWithMapping[index] + val (candidateSignature, argumentMapping, isApplicableBestCandidate) = candidatesWithMapping[index] // For array set calls, we only want the index arguments in brackets, which are all except the last (the value to set). val isArraySetCall = candidateSignature.symbol.callableIdIfNonLocal?.let { @@ -205,8 +209,10 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K setValueParameter ) - // TODO: This should be changed when there are multiple candidates available; need to know which one the call is resolved to - val isCallResolvedToCandidate = candidatesWithMapping.size == 1 + // We want to highlight the candidate green if it is the only best/final candidate selected and is applicable. + // However, if there is only one candidate available, we want to highlight it green regardless of its applicability. + val shouldHighlightGreen = (isApplicableBestCandidate && !hasMultipleApplicableBestCandidates) + || candidatesWithMapping.size == 1 candidateInfo.callInfo = CallInfo( callElement, @@ -215,10 +221,10 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K argumentToParameterIndex, valueParameters.size, parameterIndexToText, - isCallResolvedToCandidate, + shouldHighlightGreen, hasTypeMismatchBeforeCurrent, highlightParameterIndex, - candidateSignature.symbol.deprecationStatus != null, + isDeprecated = candidateSignature.symbol.deprecationStatus != null, ) } } @@ -251,7 +257,7 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K } if (includeName) { - append(parameter.symbol.name) + append(parameter.name) append(": ") } @@ -388,9 +394,10 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K var isDisabledBeforeHighlight = false var hasUnmappedArgument = false var hasUnmappedArgumentBeforeCurrent = false + var lastMappedArgumentIndex = -1 + var namedMode = false val usedParameterIndices = HashSet<Int>() val text = buildString { - var namedMode = false var argumentIndex = 0 fun appendParameter( @@ -452,6 +459,7 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K argumentIndex++ continue } + lastMappedArgumentIndex = argumentIndex if (!usedParameterIndices.add(parameterIndex)) continue val shouldHighlight = parameterIndex == highlightParameterIndex @@ -463,12 +471,13 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K val parameterIndex = argumentToParameterIndex[argument] if (parameterIndex == null) { hasUnmappedArgument = true - if (argumentIndex <= currentArgumentIndex) { + if (argumentIndex < currentArgumentIndex) { hasUnmappedArgumentBeforeCurrent = true } argumentIndex++ continue } + lastMappedArgumentIndex = argumentIndex if (!usedParameterIndices.add(parameterIndex)) continue val shouldHighlight = parameterIndex == highlightParameterIndex @@ -493,14 +502,15 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K } } - val backgroundColor = if (isCallResolvedToCandidate) GREEN_BACKGROUND else context.defaultParameterColor + val backgroundColor = if (shouldHighlightGreen) GREEN_BACKGROUND else context.defaultParameterColor // Disabled when there are too many arguments. val allParametersUsed = usedParameterIndices.size == valueParameterCount val supportsTrailingCommas = callElement.languageVersionSettings.supportsFeature(LanguageFeature.TrailingCommas) val afterTrailingComma = arguments.isNotEmpty() && currentArgumentIndex == arguments.size val isInPositionToEnterArgument = !supportsTrailingCommas && afterTrailingComma - val tooManyArgs = allParametersUsed && (isInPositionToEnterArgument || hasUnmappedArgument) + val isAfterMappedArgs = currentArgumentIndex > lastMappedArgumentIndex + val tooManyArgs = allParametersUsed && (isInPositionToEnterArgument || hasUnmappedArgument) && (isAfterMappedArgs || namedMode) val isDisabled = tooManyArgs || hasTypeMismatchBeforeCurrent || hasUnmappedArgumentBeforeCurrent @@ -525,7 +535,7 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K val argumentToParameterIndex: LinkedHashMap<KtExpression, Int>, val valueParameterCount: Int, val parameterIndexToText: Map<Int, String>, - val isCallResolvedToCandidate: Boolean, + val shouldHighlightGreen: Boolean, val hasTypeMismatchBeforeCurrent: Boolean, val highlightParameterIndex: Int?, val isDeprecated: Boolean, diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinHighLevelTypeArgumentInfoHandler.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinHighLevelTypeArgumentInfoHandler.kt index b39699a0f0c5..14c502dadae3 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinHighLevelTypeArgumentInfoHandler.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinHighLevelTypeArgumentInfoHandler.kt @@ -51,7 +51,7 @@ class KotlinHighLevelFunctionTypeArgumentInfoHandler : KotlinHighLevelTypeArgume // will NOT return a KtCall because there is no FirFunctionCall there. We find the symbols using the callee name instead. val reference = callElement.calleeExpression?.references?.singleOrNull() as? KtSimpleNameReference ?: return null val parent = callElement.parent - val receiver = if (parent is KtDotQualifiedExpression && parent.selectorExpression == callElement) { + val receiver = if (parent is KtQualifiedExpression && parent.selectorExpression == callElement) { parent.receiverExpression } else null val fileSymbol = callElement.containingKtFile.getFileSymbol() diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/parameterInfo/utils.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/parameterInfo/utils.kt index 73f93f18a729..7671b35b42e0 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/parameterInfo/utils.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/parameterInfo/utils.kt @@ -2,39 +2,51 @@ package org.jetbrains.kotlin.idea.parameterInfo import org.jetbrains.kotlin.analysis.api.KtAnalysisSession -import org.jetbrains.kotlin.analysis.api.calls.KtFunctionCall -import org.jetbrains.kotlin.analysis.api.calls.calls -import org.jetbrains.kotlin.analysis.api.calls.symbol +import org.jetbrains.kotlin.analysis.api.calls.* import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithVisibility +import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability import org.jetbrains.kotlin.psi.* // Analogous to Call.resolveCandidates() in plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/Utils.kt -internal fun KtAnalysisSession.resolveCallCandidates(callElement: KtElement): List<CandidateWithMapping> { - // TODO: FE 1.0 plugin collects all candidates (i.e., all overloads), even if arguments do not match. Not just resolved call. - // See Call.resolveCandidates() in core/src/org/jetbrains/kotlin/idea/core/Utils.kt. Note `replaceCollectAllCandidates(true)`. - - val (resolvedCall, receiver) = when (callElement) { +internal fun KtAnalysisSession.collectCallCandidates(callElement: KtElement): List<CandidateWithMapping> { + val (candidates, receiver) = when (callElement) { is KtCallElement -> { val parent = callElement.parent - val receiver = if (parent is KtDotQualifiedExpression && parent.selectorExpression == callElement) { + val receiver = if (parent is KtQualifiedExpression && parent.selectorExpression == callElement) { parent.receiverExpression } else null - Pair(callElement.resolveCall(), receiver) + callElement.collectCallCandidates() to receiver } - is KtArrayAccessExpression -> Pair(callElement.resolveCall(), callElement.arrayExpression) + is KtArrayAccessExpression -> callElement.collectCallCandidates() to callElement.arrayExpression else -> return emptyList() } + if (candidates.isEmpty()) return emptyList() val fileSymbol = callElement.containingKtFile.getFileSymbol() - return resolvedCall.calls.filterIsInstance<KtFunctionCall<*>>() - .filter { filterCandidate(it.symbol, callElement, fileSymbol, receiver) } - .map { - CandidateWithMapping( - it.partiallyAppliedSymbol.signature, - it.argumentMapping, - ) - } + + return candidates.filter { + filterCandidate(it, callElement, fileSymbol, receiver) + }.map { + val functionCall = it.candidate as KtFunctionCall<*> + CandidateWithMapping( + functionCall.partiallyAppliedSymbol.signature, + functionCall.argumentMapping, + isApplicableBestCandidate = it is KtApplicableCallCandidateInfo && it.isInBestCandidates + ) + } +} + +private fun KtAnalysisSession.filterCandidate( + candidateInfo: KtCallCandidateInfo, + callElement: KtElement, + fileSymbol: KtFileSymbol, + receiver: KtExpression? +): Boolean { + val candidateCall = candidateInfo.candidate + if (candidateCall !is KtFunctionCall<*>) return false + val candidateSymbol = candidateCall.partiallyAppliedSymbol.signature.symbol + return filterCandidate(candidateSymbol, callElement, fileSymbol, receiver) } internal fun KtAnalysisSession.filterCandidate( @@ -54,8 +66,20 @@ internal fun KtAnalysisSession.filterCandidate( } if (receiver != null && candidateSymbol is KtCallableSymbol) { - // Filter out candidates with wrong receiver - val receiverType = receiver.getKtType() ?: error("Receiver should have a KtType") + // We want only the candidates that match the receiver type. E.g., if you have code like this: + // ``` + // fun String.foo() {} + // fun Int.foo() {} + // fun call(i: Int?) { + // <expr>i?.foo()</expr> + // } + // ``` + // The available candidates are `String.foo()` and `Int.foo()`. When checking the receiver types for safe calls, we want to compare + // the non-nullable receiver type against the candidate receiver type. E.g., that `Int` (and not the type of `i` which is `Int?`) + // is subtype of `Int` (the candidate receiver type). + val isSafeCall = receiver.parent is KtSafeQualifiedExpression + val receiverType = receiver.getKtType()?.let { if (isSafeCall) it.withNullability(KtTypeNullability.NON_NULLABLE) else it } + ?: error("Receiver should have a KtType") val candidateReceiverType = candidateSymbol.receiverType if (candidateReceiverType != null && receiverType.isNotSubTypeOf(candidateReceiverType)) return false } @@ -69,4 +93,5 @@ internal fun KtAnalysisSession.filterCandidate( internal data class CandidateWithMapping( val candidate: KtFunctionLikeSignature<KtFunctionLikeSymbol>, val argumentMapping: LinkedHashMap<KtExpression, KtVariableLikeSignature<KtValueParameterSymbol>>, + val isApplicableBestCandidate: Boolean, )
\ No newline at end of file diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/SpecifySuperTypeFixFactory.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/SpecifySuperTypeFixFactory.kt index 380f8c484b88..caa7ec11a4d2 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/SpecifySuperTypeFixFactory.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/SpecifySuperTypeFixFactory.kt @@ -32,7 +32,7 @@ object SpecifySuperTypeFixFactory { class Input(val superTypes: List<TypeStringWithoutArgs>) : HLApplicatorInput val applicator = applicator<KtSuperExpression, Input> { - familyAndActionName(KotlinBundle.lazyMessage("specify.super.type")) + familyAndActionName(KotlinBundle.lazyMessage("intention.name.specify.supertype")) applyToWithEditorRequired { psi, input, project, editor -> when (input.superTypes.size) { 0 -> return@applyToWithEditorRequired @@ -46,7 +46,7 @@ object SpecifySuperTypeFixFactory { } private fun KtSuperExpression.specifySuperType(superType: TypeStringWithoutArgs) { - project.executeWriteCommand(KotlinBundle.getMessage("specify.super.type")) { + project.executeWriteCommand(KotlinBundle.message("intention.name.specify.supertype")) { val label = this.labelQualifier?.text ?: "" val replaced = replace(KtPsiFactory(this).createExpression("super<${superType.longTypeRepresentation}>$label")) as KtSuperExpression @@ -55,7 +55,7 @@ object SpecifySuperTypeFixFactory { } private fun createListPopupStep(superExpression: KtSuperExpression, superTypes: List<TypeStringWithoutArgs>): ListPopupStep<*> { - return object : BaseListPopupStep<TypeStringWithoutArgs>(KotlinBundle.getMessage("choose.super.type"), superTypes) { + return object : BaseListPopupStep<TypeStringWithoutArgs>(KotlinBundle.getMessage("popup.title.choose.supertype"), superTypes) { override fun isAutoSelectionEnabled() = false override fun onChosen(selectedValue: TypeStringWithoutArgs, finalChoice: Boolean): PopupStep<*>? { diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/search/KotlinSearchUsagesSupportFirImpl.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/search/KotlinSearchUsagesSupportFirImpl.kt index 26a162e2a8c3..696c6b80ee38 100644 --- a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/search/KotlinSearchUsagesSupportFirImpl.kt +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/search/KotlinSearchUsagesSupportFirImpl.kt @@ -11,27 +11,30 @@ import com.intellij.psi.search.SearchScope import com.intellij.psi.search.searches.DefinitionsScopedSearch import com.intellij.psi.search.searches.OverridingMethodsSearch import com.intellij.util.Processor -import org.jetbrains.kotlin.asJava.classes.KtFakeLightMethod -import org.jetbrains.kotlin.asJava.unwrapped -import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.idea.core.isInheritable import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.analyse import org.jetbrains.kotlin.analysis.api.analyseWithReadAction +import org.jetbrains.kotlin.analysis.api.calls.KtDelegatedConstructorCall +import org.jetbrains.kotlin.analysis.api.calls.symbol import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality import org.jetbrains.kotlin.analysis.api.tokens.HackToForceAllowRunningAnalyzeOnEDT import org.jetbrains.kotlin.analysis.api.tokens.hackyAllowRunningOnEdt +import org.jetbrains.kotlin.asJava.classes.KtFakeLightMethod +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.idea.core.isInheritable import org.jetbrains.kotlin.idea.search.declarationsSearch.toPossiblyFakeLightMethods -import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions import org.jetbrains.kotlin.idea.search.usagesSearch.getDefaultImports import org.jetbrains.kotlin.idea.stubindex.KotlinTypeAliasShortNameIndex import org.jetbrains.kotlin.idea.util.ProjectRootsUtil +import org.jetbrains.kotlin.idea.util.withResolvedCall import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.resolve.ImportPath class KotlinSearchUsagesSupportFirImpl : KotlinSearchUsagesSupport { @@ -51,13 +54,6 @@ class KotlinSearchUsagesSupportFirImpl : KotlinSearchUsagesSupport { return false } - override fun <T : PsiNamedElement> filterDataClassComponentsIfDisabled( - elements: List<T>, - kotlinOptions: KotlinReferencesSearchOptions - ): List<T> { - return emptyList() - } - override fun isCallableOverrideUsage(reference: PsiReference, declaration: KtNamedDeclaration): Boolean { return false } @@ -242,10 +238,15 @@ class KotlinSearchUsagesSupportFirImpl : KotlinSearchUsagesSupport { } override fun createConstructorHandle(ktDeclaration: KtDeclaration): KotlinSearchUsagesSupport.ConstructorCallHandle { - //TODO FIR: This is the stub. Need to implement return object : KotlinSearchUsagesSupport.ConstructorCallHandle { override fun referencedTo(element: KtElement): Boolean { - return false + val callExpression = element.getNonStrictParentOfType<KtCallElement>() ?: return false + return withResolvedCall(callExpression) { call -> + when (call) { + is KtDelegatedConstructorCall -> call.symbol == ktDeclaration.getSymbol() + else -> false + } + } ?: false } } } @@ -258,4 +259,4 @@ class KotlinSearchUsagesSupportFirImpl : KotlinSearchUsagesSupport { } } } -}
\ No newline at end of file +} diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/search/ideaExtensions/FirKotlinTargetElementEvaluator.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/search/ideaExtensions/FirKotlinTargetElementEvaluator.kt new file mode 100644 index 000000000000..8d4a9d4e13de --- /dev/null +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/search/ideaExtensions/FirKotlinTargetElementEvaluator.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.search.ideaExtensions + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference + +class FirKotlinTargetElementEvaluator : KotlinTargetElementEvaluator() { + override fun findLambdaOpenLBraceForGeneratedIt(ref: PsiReference): PsiElement? { + // TODO: implement + return null + } + + override fun findReceiverForThisInExtensionFunction(ref: PsiReference): PsiElement? { + // TODO: implement + return null + } +} diff --git a/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/util/findUsagesUtils.kt b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/util/findUsagesUtils.kt new file mode 100644 index 000000000000..df59b5bed1a4 --- /dev/null +++ b/plugins/kotlin/fir/src/org/jetbrains/kotlin/idea/util/findUsagesUtils.kt @@ -0,0 +1,14 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.kotlin.idea.util + +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.analyseWithReadAction +import org.jetbrains.kotlin.analysis.api.calls.KtCall +import org.jetbrains.kotlin.analysis.api.calls.calls +import org.jetbrains.kotlin.psi.KtElement + +inline fun <R> withResolvedCall(element: KtElement, crossinline block: KtAnalysisSession.(KtCall) -> R): R? { + return analyseWithReadAction(element) { + element.resolveCall()?.calls?.singleOrNull()?.let { block(it) } + } +} |