[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
|
||||
|
||||
import com.intellij.ide.IdeBundle
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiReference
|
||||
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.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 {
|
||||
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> {
|
||||
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 {
|
||||
|
||||
+3
-1
@@ -41,12 +41,14 @@ abstract class KtAnalysisSession(override val token: ValidityToken) : ValidityTo
|
||||
protected abstract val symbolProvider: KtSymbolProvider
|
||||
protected abstract val callResolver: KtCallResolver
|
||||
protected abstract val completionCandidateChecker: KtCompletionCandidateChecker
|
||||
|
||||
protected abstract val symbolDeclarationOverridesProvider: KtSymbolDeclarationOverridesProvider
|
||||
|
||||
/// TODO: get rid of
|
||||
@Deprecated("Used only in completion now, temporary")
|
||||
abstract fun createContextDependentCopy(): KtAnalysisSession
|
||||
|
||||
fun KtCallableSymbol.getOverriddenSymbols(containingDeclaration: KtClassOrObjectSymbol): List<KtCallableSymbol> =
|
||||
symbolDeclarationOverridesProvider.getOverriddenSymbols(this, containingDeclaration)
|
||||
|
||||
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 =
|
||||
KtFirSymbolProvider(this, firResolveState.firIdeLibrariesSession.firSymbolProvider, firResolveState, firSymbolBuilder, token)
|
||||
override val completionCandidateChecker: KtCompletionCandidateChecker by threadLocal { KtFirCompletionCandidateChecker(this, token) }
|
||||
|
||||
override val symbolDeclarationOverridesProvider: KtSymbolDeclarationOverridesProvider
|
||||
by threadLocal { KtFirSymbolDeclarationOverridesProvider(this, token) }
|
||||
|
||||
override fun createContextDependentCopy(): KtAnalysisSession {
|
||||
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