[FIR IDE] Add base implementation of find usages of overrides
This commit is contained in:
+74
-1
@@ -5,11 +5,26 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.findUsages
|
package org.jetbrains.kotlin.idea.findUsages
|
||||||
|
|
||||||
|
import com.intellij.ide.IdeBundle
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.ui.Messages
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiMethod
|
||||||
import com.intellij.psi.PsiReference
|
import com.intellij.psi.PsiReference
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
|
import org.jetbrains.kotlin.idea.core.util.showYesNoCancelDialog
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.analyseInModalWindow
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.CHECK_SUPER_METHODS_YES_NO_DIALOG
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.formatPsiClass
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||||
|
|
||||||
class KotlinFindUsagesSupportFirImpl : KotlinFindUsagesSupport {
|
class KotlinFindUsagesSupportFirImpl : KotlinFindUsagesSupport {
|
||||||
override fun isCallReceiverRefersToCompanionObject(element: KtElement, companionObject: KtObjectDeclaration): Boolean {
|
override fun isCallReceiverRefersToCompanionObject(element: KtElement, companionObject: KtObjectDeclaration): Boolean {
|
||||||
@@ -33,7 +48,65 @@ class KotlinFindUsagesSupportFirImpl : KotlinFindUsagesSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun checkSuperMethods(declaration: KtDeclaration, ignore: Collection<PsiElement>?, actionString: String): List<PsiElement> {
|
override fun checkSuperMethods(declaration: KtDeclaration, ignore: Collection<PsiElement>?, actionString: String): List<PsiElement> {
|
||||||
return listOf(declaration)
|
|
||||||
|
if (!declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return listOf(declaration)
|
||||||
|
|
||||||
|
data class AnalyzedModel(
|
||||||
|
val declaredClassRender: String,
|
||||||
|
val overriddenDeclarationsAndRenders: Map<PsiElement, String>
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getClassDescription(overriddenElement: PsiElement, containingSymbol: KtSymbolWithKind?): String =
|
||||||
|
when (overriddenElement) {
|
||||||
|
is KtNamedFunction, is KtProperty, is KtParameter -> (containingSymbol as? KtNamedSymbol)?.name?.asString() ?: "Unknown" //TODO render symbols
|
||||||
|
is PsiMethod -> {
|
||||||
|
val psiClass = overriddenElement.containingClass ?: error("Invalid element: ${overriddenElement.text}")
|
||||||
|
formatPsiClass(psiClass, markAsJava = true, inCode = false)
|
||||||
|
}
|
||||||
|
else -> error("Unexpected element: ${overriddenElement.getElementTextWithContext()}")
|
||||||
|
}.let { " $it\n" }
|
||||||
|
|
||||||
|
|
||||||
|
val analyzeResult = analyseInModalWindow(declaration, KotlinBundle.message("find.usages.progress.text.declaration.superMethods")) {
|
||||||
|
(declaration.getSymbol() as? KtCallableSymbol)?.let { callableSymbol ->
|
||||||
|
((callableSymbol as? KtSymbolWithKind)?.getContainingSymbol() as? KtClassOrObjectSymbol)?.let { containingClass ->
|
||||||
|
val overriddenSymbols = callableSymbol.getOverriddenSymbols(containingClass)
|
||||||
|
|
||||||
|
val renderToPsi = overriddenSymbols.mapNotNull {
|
||||||
|
it.psi?.let { psi ->
|
||||||
|
psi to getClassDescription(psi, (it as? KtSymbolWithKind)?.getContainingSymbol())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val filteredDeclarations =
|
||||||
|
if (ignore != null) renderToPsi.filter { ignore.contains(it.first) } else renderToPsi
|
||||||
|
|
||||||
|
val renderedClass = containingClass.name.asString() //TODO render class
|
||||||
|
|
||||||
|
AnalyzedModel(renderedClass, filteredDeclarations.toMap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ?: return listOf(declaration)
|
||||||
|
|
||||||
|
if (analyzeResult.overriddenDeclarationsAndRenders.isEmpty()) return listOf(declaration)
|
||||||
|
|
||||||
|
val message = KotlinBundle.message(
|
||||||
|
"override.declaration.x.overrides.y.in.class.list",
|
||||||
|
analyzeResult.declaredClassRender,
|
||||||
|
"\n${analyzeResult.overriddenDeclarationsAndRenders.values.joinToString(separator = "")}",
|
||||||
|
actionString
|
||||||
|
)
|
||||||
|
|
||||||
|
val exitCode = showYesNoCancelDialog(
|
||||||
|
CHECK_SUPER_METHODS_YES_NO_DIALOG,
|
||||||
|
declaration.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon(), Messages.YES
|
||||||
|
)
|
||||||
|
|
||||||
|
return when (exitCode) {
|
||||||
|
Messages.YES -> listOf(declaration) + analyzeResult.overriddenDeclarationsAndRenders.keys
|
||||||
|
Messages.NO -> listOf(declaration)
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sourcesAndLibraries(delegate: GlobalSearchScope, project: Project): GlobalSearchScope {
|
override fun sourcesAndLibraries(delegate: GlobalSearchScope, project: Project): GlobalSearchScope {
|
||||||
|
|||||||
+3
-1
@@ -41,12 +41,14 @@ abstract class KtAnalysisSession(override val token: ValidityToken) : ValidityTo
|
|||||||
protected abstract val symbolProvider: KtSymbolProvider
|
protected abstract val symbolProvider: KtSymbolProvider
|
||||||
protected abstract val callResolver: KtCallResolver
|
protected abstract val callResolver: KtCallResolver
|
||||||
protected abstract val completionCandidateChecker: KtCompletionCandidateChecker
|
protected abstract val completionCandidateChecker: KtCompletionCandidateChecker
|
||||||
|
protected abstract val symbolDeclarationOverridesProvider: KtSymbolDeclarationOverridesProvider
|
||||||
|
|
||||||
/// TODO: get rid of
|
/// TODO: get rid of
|
||||||
@Deprecated("Used only in completion now, temporary")
|
@Deprecated("Used only in completion now, temporary")
|
||||||
abstract fun createContextDependentCopy(): KtAnalysisSession
|
abstract fun createContextDependentCopy(): KtAnalysisSession
|
||||||
|
|
||||||
|
fun KtCallableSymbol.getOverriddenSymbols(containingDeclaration: KtClassOrObjectSymbol): List<KtCallableSymbol> =
|
||||||
|
symbolDeclarationOverridesProvider.getOverriddenSymbols(this, containingDeclaration)
|
||||||
|
|
||||||
fun KtExpression.getSmartCasts(): Collection<KtType> = smartCastProvider.getSmartCastedToTypes(this)
|
fun KtExpression.getSmartCasts(): Collection<KtType> = smartCastProvider.getSmartCastedToTypes(this)
|
||||||
|
|
||||||
|
|||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.frontend.api.components
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
|
||||||
|
|
||||||
|
abstract class KtSymbolDeclarationOverridesProvider : KtAnalysisSessionComponent() {
|
||||||
|
/**
|
||||||
|
* Returns symbols that overridden by requested
|
||||||
|
*/
|
||||||
|
abstract fun <T : KtSymbol> getOverriddenSymbols(
|
||||||
|
callableSymbol: T,
|
||||||
|
containingDeclaration: KtClassOrObjectSymbol
|
||||||
|
): List<KtCallableSymbol>
|
||||||
|
|
||||||
|
//abstract fun getOverriddenSymbols(callableSymbol: KtCallableSymbol, containingDeclaration: KtClassOrObjectSymbol): List<KtCallableSymbol>
|
||||||
|
}
|
||||||
+2
-1
@@ -42,7 +42,8 @@ private constructor(
|
|||||||
override val symbolProvider: KtSymbolProvider =
|
override val symbolProvider: KtSymbolProvider =
|
||||||
KtFirSymbolProvider(this, firResolveState.firIdeLibrariesSession.firSymbolProvider, firResolveState, firSymbolBuilder, token)
|
KtFirSymbolProvider(this, firResolveState.firIdeLibrariesSession.firSymbolProvider, firResolveState, firSymbolBuilder, token)
|
||||||
override val completionCandidateChecker: KtCompletionCandidateChecker by threadLocal { KtFirCompletionCandidateChecker(this, token) }
|
override val completionCandidateChecker: KtCompletionCandidateChecker by threadLocal { KtFirCompletionCandidateChecker(this, token) }
|
||||||
|
override val symbolDeclarationOverridesProvider: KtSymbolDeclarationOverridesProvider
|
||||||
|
by threadLocal { KtFirSymbolDeclarationOverridesProvider(this, token) }
|
||||||
|
|
||||||
override fun createContextDependentCopy(): KtAnalysisSession {
|
override fun createContextDependentCopy(): KtAnalysisSession {
|
||||||
check(!isContextSession) { "Cannot create context-dependent copy of KtAnalysis session from a context dependent one" }
|
check(!isContextSession) { "Cannot create context-dependent copy of KtAnalysis session from a context dependent one" }
|
||||||
|
|||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.frontend.api.fir.components
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.*
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtSymbolDeclarationOverridesProvider
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirClassOrObjectSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||||
|
|
||||||
|
internal class KtFirSymbolDeclarationOverridesProvider(
|
||||||
|
override val analysisSession: KtFirAnalysisSession,
|
||||||
|
override val token: ValidityToken,
|
||||||
|
) : KtSymbolDeclarationOverridesProvider(), KtFirAnalysisSessionComponent {
|
||||||
|
|
||||||
|
private fun FirTypeScope.processCallableByName(declaration: FirDeclaration) = when (declaration) {
|
||||||
|
is FirSimpleFunction -> processFunctionsByName(declaration.name) { }
|
||||||
|
is FirProperty -> processPropertiesByName(declaration.name) { }
|
||||||
|
else -> error { " Invalid FIR symbol to process " }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirTypeScope.processOverriddenDeclarations(
|
||||||
|
declaration: FirDeclaration,
|
||||||
|
processor: (FirCallableDeclaration<*>) -> ProcessorAction
|
||||||
|
) = when (declaration) {
|
||||||
|
is FirSimpleFunction -> processOverriddenFunctions(declaration.symbol) { processor.invoke(it.fir) }
|
||||||
|
is FirProperty -> processOverriddenProperties(declaration.symbol) { processor.invoke(it.fir) }
|
||||||
|
else -> error { " Invalid FIR symbol to process " }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : KtSymbol> getOverriddenSymbols(
|
||||||
|
callableSymbol: T,
|
||||||
|
containingDeclaration: KtClassOrObjectSymbol
|
||||||
|
): List<KtCallableSymbol> {
|
||||||
|
|
||||||
|
check(callableSymbol is KtFirSymbol<*>)
|
||||||
|
check(containingDeclaration is KtFirClassOrObjectSymbol)
|
||||||
|
|
||||||
|
return callableSymbol.firRef.withFir(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) { firCallableElement ->
|
||||||
|
|
||||||
|
containingDeclaration.firRef.withFir(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) { containingDeclaration ->
|
||||||
|
|
||||||
|
val firTypeScope = containingDeclaration.unsubstitutedScope(
|
||||||
|
containingDeclaration.session,
|
||||||
|
ScopeSession()
|
||||||
|
)
|
||||||
|
|
||||||
|
val overriddenElement = mutableSetOf<KtCallableSymbol>()
|
||||||
|
firTypeScope.processCallableByName(firCallableElement)
|
||||||
|
firTypeScope.processOverriddenDeclarations(firCallableElement) { overriddenDeclaration ->
|
||||||
|
val ktSymbol = analysisSession.firSymbolBuilder.buildCallableSymbol(overriddenDeclaration)
|
||||||
|
overriddenElement.add(ktSymbol)
|
||||||
|
ProcessorAction.NEXT
|
||||||
|
}
|
||||||
|
|
||||||
|
overriddenElement.toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user