FIR IDE: specify behaviour of HL API getOverriddenSymbols
- Split it into two functions getAllOverriddenSymbols and getDirectlyOverriddenSymbols - Implement tests for getOverriddenSymbols - temporary mute inheritance.kt light classes test
This commit is contained in:
@@ -29,5 +29,3 @@ private class Private {
|
||||
override val overridesNothing: Boolean
|
||||
get() = false
|
||||
}
|
||||
|
||||
// FIR_COMPARISON
|
||||
@@ -89,6 +89,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.AbstractSessionsInva
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.trackers.AbstractProjectWideOutOfBlockKotlinModificationTrackerTest
|
||||
import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.AbstractExpectedExpressionTypeTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.AbstractOverriddenDeclarationProviderTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.AbstractReturnExpressionTargetTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.AbstractResolveCallTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.scopes.AbstractFileScopeTest
|
||||
@@ -1040,6 +1041,10 @@ fun main(args: Array<String>) {
|
||||
testClass<AbstractExpectedExpressionTypeTest> {
|
||||
model("components/expectedExpressionType")
|
||||
}
|
||||
|
||||
testClass<AbstractOverriddenDeclarationProviderTest> {
|
||||
model("components/overridenDeclarations")
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("idea/idea-frontend-fir/idea-fir-low-level-api/tests", "idea/testData") {
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ class KotlinFindUsagesSupportFirImpl : KotlinFindUsagesSupport {
|
||||
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 overriddenSymbols = callableSymbol.getAllOverriddenSymbols()
|
||||
|
||||
val renderToPsi = overriddenSymbols.mapNotNull {
|
||||
it.psi?.let { psi ->
|
||||
|
||||
+3
-3
@@ -118,10 +118,10 @@ object ChangeTypeQuickFix {
|
||||
callable: KtCallableSymbol,
|
||||
type: KtType
|
||||
): KtCallableSymbol? {
|
||||
val overriddenSymbols = callable.getOverriddenSymbols()
|
||||
val overriddenSymbols = callable.getDirectlyOverriddenSymbols()
|
||||
return overriddenSymbols
|
||||
.singleOrNull { overridden ->
|
||||
overridden.origin != KtSymbolOrigin.INTERSECTION_OVERRIDE && !type.isSubTypeOf(overridden.annotatedType.type)
|
||||
!type.isSubTypeOf(overridden.annotatedType.type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ object ChangeTypeQuickFix {
|
||||
|
||||
private fun KtAnalysisSession.findLowerBoundOfOverriddenCallablesReturnTypes(symbol: KtCallableSymbol): KtType? {
|
||||
var lowestType: KtType? = null
|
||||
for (overridden in symbol.getOverriddenSymbols()) {
|
||||
for (overridden in symbol.getDirectlyOverriddenSymbols()) {
|
||||
val overriddenType = overridden.annotatedType.type
|
||||
when {
|
||||
lowestType == null || overriddenType isSubTypeOf lowestType -> {
|
||||
|
||||
+23
-6
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* The entry point into all frontend-related work. Has the following contracts:
|
||||
@@ -59,12 +58,30 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali
|
||||
|
||||
abstract fun createContextDependentCopy(originalKtFile: KtFile, fakeKtElement: KtElement): KtAnalysisSession
|
||||
|
||||
//TODO get rid of it
|
||||
fun KtCallableSymbol.getOverriddenSymbols(containingDeclaration: KtClassOrObjectSymbol): List<KtCallableSymbol> =
|
||||
symbolDeclarationOverridesProvider.getOverriddenSymbols(this, containingDeclaration)
|
||||
|
||||
fun KtCallableSymbol.getOverriddenSymbols(): List<KtCallableSymbol> =
|
||||
symbolDeclarationOverridesProvider.getOverriddenSymbols(this)
|
||||
/**
|
||||
* Return a list of **all** symbols which are overridden by symbol
|
||||
*
|
||||
* E.g, if we have `A.foo` overrides `B.foo` overrides `C.foo`, all two super declarations `B.foo`, `C.foo` will be returned
|
||||
*
|
||||
* Unwraps substituted overridden symbols (see [org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbolOrigin.INTERSECTION_OVERRIDE])
|
||||
*
|
||||
* @see getDirectlyOverriddenSymbols
|
||||
*/
|
||||
fun KtCallableSymbol.getAllOverriddenSymbols(): List<KtCallableSymbol> =
|
||||
symbolDeclarationOverridesProvider.getAllOverriddenSymbols(this)
|
||||
|
||||
/**
|
||||
* Return a list of symbols which are **directly** overridden by symbol
|
||||
**
|
||||
* E.g, if we have `A.foo` overrides `B.foo` overrides `C.foo`, only declarations directly overriden `B.foo` will be returned
|
||||
*
|
||||
* Unwraps substituted overridden symbols (see [org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbolOrigin.INTERSECTION_OVERRIDE])
|
||||
*
|
||||
* @see getAllOverriddenSymbols
|
||||
*/
|
||||
fun KtCallableSymbol.getDirectlyOverriddenSymbols(): List<KtCallableSymbol> =
|
||||
symbolDeclarationOverridesProvider.getDirectlyOverriddenSymbols(this)
|
||||
|
||||
fun KtCallableSymbol.getIntersectionOverriddenSymbols(): Collection<KtCallableSymbol> =
|
||||
symbolDeclarationOverridesProvider.getIntersectionOverriddenSymbols(this)
|
||||
|
||||
+5
-5
@@ -11,20 +11,20 @@ import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
|
||||
|
||||
abstract class KtSymbolDeclarationOverridesProvider : KtAnalysisSessionComponent() {
|
||||
/**
|
||||
* Returns symbols that overridden by requested
|
||||
* Returns symbols that are overridden by requested
|
||||
*/
|
||||
abstract fun <T : KtSymbol> getOverriddenSymbols(
|
||||
abstract fun <T : KtSymbol> getAllOverriddenSymbols(
|
||||
callableSymbol: T,
|
||||
containingDeclaration: KtClassOrObjectSymbol
|
||||
): List<KtCallableSymbol>
|
||||
|
||||
/**
|
||||
* Returns symbols that overridden by requested
|
||||
* Returns symbols that are overridden by requested
|
||||
*/
|
||||
abstract fun <T : KtSymbol> getOverriddenSymbols(
|
||||
abstract fun <T : KtSymbol> getDirectlyOverriddenSymbols(
|
||||
callableSymbol: T,
|
||||
): List<KtCallableSymbol>
|
||||
|
||||
|
||||
/**
|
||||
* If [symbol] origin is [org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbolOrigin.INTERSECTION_OVERRIDE]
|
||||
* Then returns the symbols which [symbol] overrides, otherwise empty collection
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ internal class FirLightClassForSymbol(
|
||||
var visibility = (symbol as? KtSymbolWithVisibility)?.visibility
|
||||
|
||||
analyzeWithSymbolAsContext(symbol) {
|
||||
for (overriddenSymbol in symbol.getOverriddenSymbols(classOrObjectSymbol)) {
|
||||
for (overriddenSymbol in symbol.getAllOverriddenSymbols()) {
|
||||
val newVisibility = (overriddenSymbol as? KtSymbolWithVisibility)?.visibility
|
||||
if (newVisibility != null) {
|
||||
visibility = newVisibility
|
||||
|
||||
+78
-25
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverrideFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverridePropertySymbol
|
||||
import org.jetbrains.kotlin.fir.unwrapFakeOverrides
|
||||
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
|
||||
@@ -27,53 +26,107 @@ internal class KtFirSymbolDeclarationOverridesProvider(
|
||||
override val token: ValidityToken,
|
||||
) : KtSymbolDeclarationOverridesProvider(), KtFirAnalysisSessionComponent {
|
||||
|
||||
override fun <T : KtSymbol> getAllOverriddenSymbols(
|
||||
callableSymbol: T,
|
||||
): List<KtCallableSymbol> {
|
||||
val overriddenElement = mutableSetOf<FirCallableSymbol<*>>()
|
||||
processOverrides(callableSymbol) { firTypeScope, firCallableDeclaration ->
|
||||
firTypeScope.processAllOverriddenDeclarations(firCallableDeclaration) { overriddenDeclaration ->
|
||||
overriddenDeclaration.symbol.collectIntersectionOverridesSymbolsTo(overriddenElement)
|
||||
}
|
||||
}
|
||||
return overriddenElement.map { analysisSession.firSymbolBuilder.buildCallableSymbol(it.fir) }
|
||||
}
|
||||
|
||||
override fun <T : KtSymbol> getDirectlyOverriddenSymbols(callableSymbol: T): List<KtCallableSymbol> {
|
||||
val overriddenElement = mutableSetOf<FirCallableSymbol<*>>()
|
||||
processOverrides(callableSymbol) { firTypeScope, firCallableDeclaration ->
|
||||
firTypeScope.processDirectOverriddenDeclarations(firCallableDeclaration) { overriddenDeclaration ->
|
||||
overriddenDeclaration.symbol.collectIntersectionOverridesSymbolsTo(overriddenElement)
|
||||
}
|
||||
}
|
||||
return overriddenElement.map { analysisSession.firSymbolBuilder.buildCallableSymbol(it.fir) }
|
||||
}
|
||||
|
||||
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: ${declaration::class}" }
|
||||
}
|
||||
|
||||
private fun FirTypeScope.processOverriddenDeclarations(
|
||||
private fun FirTypeScope.processAllOverriddenDeclarations(
|
||||
declaration: FirDeclaration,
|
||||
processor: (FirCallableDeclaration<*>) -> ProcessorAction
|
||||
processor: (FirCallableDeclaration<*>) -> Unit
|
||||
) = when (declaration) {
|
||||
is FirSimpleFunction -> processOverriddenFunctions(declaration.symbol) { processor.invoke(it.fir) }
|
||||
is FirProperty -> processOverriddenProperties(declaration.symbol) { processor.invoke(it.fir) }
|
||||
is FirSimpleFunction -> processOverriddenFunctions(declaration.symbol) { symbol ->
|
||||
processor.invoke(symbol.fir)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
is FirProperty -> processOverriddenProperties(declaration.symbol) { symbol ->
|
||||
processor.invoke(symbol.fir)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
else -> error { "Invalid FIR symbol to process: ${declaration::class}" }
|
||||
}
|
||||
|
||||
override fun <T : KtSymbol> getOverriddenSymbols(
|
||||
callableSymbol: T,
|
||||
containingDeclaration: KtClassOrObjectSymbol
|
||||
): List<KtCallableSymbol> {
|
||||
private fun FirTypeScope.processDirectOverriddenDeclarations(
|
||||
declaration: FirDeclaration,
|
||||
processor: (FirCallableDeclaration<*>) -> Unit
|
||||
) = when (declaration) {
|
||||
is FirSimpleFunction -> processDirectOverriddenFunctionsWithBaseScope(declaration.symbol) { symbol, _ ->
|
||||
processor.invoke(symbol.fir)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
is FirProperty -> processDirectOverriddenPropertiesWithBaseScope(declaration.symbol) { symbol, _ ->
|
||||
processor.invoke(symbol.fir)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
else -> error { "Invalid FIR symbol to process: ${declaration::class}" }
|
||||
}
|
||||
|
||||
check(callableSymbol is KtFirSymbol<*>)
|
||||
private inline fun <T : KtSymbol> processOverrides(
|
||||
callableSymbol: T,
|
||||
crossinline process: (FirTypeScope, FirDeclaration) -> Unit
|
||||
) {
|
||||
require(callableSymbol is KtFirSymbol<*>)
|
||||
val containingDeclaration = with(analysisSession) {
|
||||
(callableSymbol as? KtSymbolWithKind)?.getContainingSymbol() as? KtClassOrObjectSymbol
|
||||
} ?: return
|
||||
check(containingDeclaration is KtFirClassOrObjectSymbol)
|
||||
|
||||
return containingDeclaration.firRef.withFir(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) { firContainer ->
|
||||
callableSymbol.firRef.withFirUnsafe { firCallableElement ->
|
||||
processOverrides(containingDeclaration, callableSymbol, process)
|
||||
}
|
||||
|
||||
private inline fun processOverrides(
|
||||
containingDeclaration: KtFirClassOrObjectSymbol,
|
||||
callableSymbol: KtFirSymbol<*>,
|
||||
crossinline process: (FirTypeScope, FirDeclaration) -> Unit
|
||||
) {
|
||||
containingDeclaration.firRef.withFir(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) { firContainer ->
|
||||
callableSymbol.firRef.withFirUnsafe { firCallableDeclaration ->
|
||||
val firTypeScope = firContainer.unsubstitutedScope(
|
||||
firContainer.session,
|
||||
ScopeSession(),
|
||||
withForcedTypeCalculator = false
|
||||
)
|
||||
|
||||
val overriddenElement = mutableSetOf<KtCallableSymbol>()
|
||||
firTypeScope.processCallableByName(firCallableElement)
|
||||
firTypeScope.processOverriddenDeclarations(firCallableElement) { overriddenDeclaration ->
|
||||
val ktSymbol = analysisSession.firSymbolBuilder.buildCallableSymbol(overriddenDeclaration)
|
||||
overriddenElement.add(ktSymbol)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
|
||||
overriddenElement.toList()
|
||||
firTypeScope.processCallableByName(firCallableDeclaration)
|
||||
process(firTypeScope, firCallableDeclaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : KtSymbol> getOverriddenSymbols(callableSymbol: T): List<KtCallableSymbol> = with(analysisSession) {
|
||||
val containingDeclaration = (callableSymbol as? KtSymbolWithKind)?.getContainingSymbol() as? KtClassOrObjectSymbol ?: return emptyList()
|
||||
getOverriddenSymbols(callableSymbol, containingDeclaration)
|
||||
private fun FirCallableSymbol<*>.collectIntersectionOverridesSymbolsTo(to: MutableCollection<FirCallableSymbol<*>>) {
|
||||
when (this) {
|
||||
is FirIntersectionOverrideFunctionSymbol -> {
|
||||
intersections.forEach { it.collectIntersectionOverridesSymbolsTo(to) }
|
||||
}
|
||||
is FirIntersectionOverridePropertySymbol -> {
|
||||
intersections.forEach { it.collectIntersectionOverridesSymbolsTo(to) }
|
||||
}
|
||||
else -> {
|
||||
to += this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIntersectionOverriddenSymbols(symbol: KtCallableSymbol): Collection<KtCallableSymbol> {
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// FILE: main.kt
|
||||
class A : B(){
|
||||
override fun fo<caret>o(x: Int): Int
|
||||
}
|
||||
|
||||
// FILE: B.kt
|
||||
abstract class B {
|
||||
open fun foo(x: Int): Int
|
||||
abstract fun foo(x: String): Int
|
||||
}
|
||||
|
||||
// RESULT
|
||||
|
||||
// ALL:
|
||||
// B.foo(x: Int): Int
|
||||
|
||||
// DIRECT:
|
||||
// B.foo(x: Int): Int
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// FILE: main.kt
|
||||
class A : B {
|
||||
override fun foo(x: Int) {
|
||||
}
|
||||
|
||||
override fun foo<caret>(x: String) {
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: B.kt
|
||||
interface B: C, D
|
||||
|
||||
// FILE: C.kt
|
||||
interface C {
|
||||
fun foo(x: Int)
|
||||
fun foo(x: String)
|
||||
}
|
||||
|
||||
// FILE: D.kt
|
||||
interface D {
|
||||
fun foo(x: Int)
|
||||
fun foo(x: String)
|
||||
}
|
||||
|
||||
// RESULT
|
||||
|
||||
// ALL:
|
||||
// C.foo(x: String): Unit
|
||||
// D.foo(x: String): Unit
|
||||
|
||||
// DIRECT:
|
||||
// C.foo(x: String): Unit
|
||||
// D.foo(x: String): Unit
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// FILE: main.kt
|
||||
class A : B, C {
|
||||
override fun foo(x: Int) {
|
||||
}
|
||||
|
||||
override fun foo<caret>(x: String) {
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: B.kt
|
||||
interface B: C, D
|
||||
|
||||
// FILE: C.kt
|
||||
interface C {
|
||||
fun foo(x: Int)
|
||||
fun foo(x: String)
|
||||
}
|
||||
|
||||
// FILE: D.kt
|
||||
interface D {
|
||||
fun foo(x: Int)
|
||||
fun foo(x: String)
|
||||
}
|
||||
|
||||
// RESULT
|
||||
|
||||
// ALL:
|
||||
// C.foo(x: String): Unit
|
||||
// D.foo(x: String): Unit
|
||||
|
||||
// DIRECT:
|
||||
// C.foo(x: String): Unit
|
||||
// D.foo(x: String): Unit
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// FILE: main.kt
|
||||
class A : B() {
|
||||
override val x<caret>: Int get() = super.x
|
||||
}
|
||||
|
||||
// FILE: B.java
|
||||
public class B extends C {
|
||||
@Override
|
||||
public int getX() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: C.kt
|
||||
abstract class C {
|
||||
abstract val x: Int
|
||||
}
|
||||
|
||||
// RESULT
|
||||
|
||||
// ALL:
|
||||
// B.x: Int
|
||||
// C.x: Int
|
||||
|
||||
// DIRECT:
|
||||
// B.x: Int
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// FILE: main.kt
|
||||
class A : B, C, D {
|
||||
override fun foo(x: Int) {
|
||||
}
|
||||
|
||||
override fun foo<caret>(x: String) {
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: B.kt
|
||||
interface B {
|
||||
fun foo(x: Int)
|
||||
fun foo(x: String)
|
||||
}
|
||||
|
||||
// FILE: C.kt
|
||||
interface C {
|
||||
fun foo(x: Int)
|
||||
fun foo(x: String)
|
||||
}
|
||||
|
||||
// FILE: D.kt
|
||||
interface D {
|
||||
fun foo(x: Int)
|
||||
fun foo(x: String)
|
||||
}
|
||||
|
||||
// RESULT
|
||||
|
||||
// ALL:
|
||||
// B.foo(x: String): Unit
|
||||
// C.foo(x: String): Unit
|
||||
// D.foo(x: String): Unit
|
||||
|
||||
// DIRECT:
|
||||
// B.foo(x: String): Unit
|
||||
// C.foo(x: String): Unit
|
||||
// D.foo(x: String): Unit
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// FILE: main.kt
|
||||
class A : B() {
|
||||
override fun foo<caret>(x: Int) {}
|
||||
}
|
||||
|
||||
// FILE: B.kt
|
||||
open class B : C() {
|
||||
override fun foo(x: Int) {}
|
||||
}
|
||||
|
||||
// FILE: C.kt
|
||||
open class C : D() {
|
||||
override fun foo(x: Int) {}
|
||||
}
|
||||
|
||||
// FILE: D.kt
|
||||
open class D {
|
||||
open fun foo(x: Int) {}
|
||||
}
|
||||
|
||||
// RESULT
|
||||
|
||||
// ALL:
|
||||
// B.foo(x: Int): Unit
|
||||
// C.foo(x: Int): Unit
|
||||
// D.foo(x: Int): Unit
|
||||
|
||||
// DIRECT:
|
||||
// B.foo(x: Int): Unit
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.frontend.api.components
|
||||
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import com.intellij.psi.util.parentsOfType
|
||||
import org.jetbrains.kotlin.idea.executeOnPooledThreadInReadAction
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.analyze
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSyntheticJavaPropertySymbol
|
||||
import org.jetbrains.kotlin.idea.test.framework.AbstractKtIdeaTest
|
||||
import org.jetbrains.kotlin.idea.test.framework.TestFileStructure
|
||||
import org.jetbrains.kotlin.idea.test.framework.TestStructureExpectedDataBlock
|
||||
import org.jetbrains.kotlin.idea.test.framework.TestStructureRenderer
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
|
||||
abstract class AbstractOverriddenDeclarationProviderTest : AbstractKtIdeaTest() {
|
||||
override fun doTestByFileStructure(fileStructure: TestFileStructure) {
|
||||
val signatures = executeOnPooledThreadInReadAction {
|
||||
analyze(fileStructure.mainKtFile) {
|
||||
val symbol = getDeclarationAtCaret().getSymbol() as KtCallableSymbol
|
||||
val allOverriddenSymbols = symbol.getAllOverriddenSymbols().map { renderSignature(it) }
|
||||
val directlyOverriddenSymbols = symbol.getDirectlyOverriddenSymbols().map { renderSignature(it) }
|
||||
listOf(
|
||||
TestStructureExpectedDataBlock("ALL:", allOverriddenSymbols),
|
||||
TestStructureExpectedDataBlock("DIRECT:", directlyOverriddenSymbols),
|
||||
)
|
||||
}
|
||||
}
|
||||
val actual = TestStructureRenderer.render(fileStructure, signatures)
|
||||
KotlinTestUtils.assertEqualsToFile(fileStructure.filePath.toFile(), actual)
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.renderSignature(symbol: KtCallableSymbol): String = buildString {
|
||||
append(getPath(symbol))
|
||||
if (symbol is KtFunctionSymbol) {
|
||||
append("(")
|
||||
symbol.valueParameters.forEachIndexed { index, parameter ->
|
||||
append(parameter.name.identifier)
|
||||
append(": ")
|
||||
append(parameter.annotatedType.type.render(KtTypeRendererOptions.SHORT_NAMES))
|
||||
if (index != symbol.valueParameters.lastIndex) {
|
||||
append(", ")
|
||||
}
|
||||
}
|
||||
append(")")
|
||||
}
|
||||
append(": ")
|
||||
append(symbol.annotatedType.type.render(KtTypeRendererOptions.SHORT_NAMES))
|
||||
}
|
||||
|
||||
private fun getPath(symbol: KtCallableSymbol): String = when (symbol) {
|
||||
is KtSyntheticJavaPropertySymbol -> symbol.callableIdIfNonLocal?.asString()!!
|
||||
else -> {
|
||||
val ktDeclaration = symbol.psi as KtDeclaration
|
||||
ktDeclaration
|
||||
.parentsOfType<KtDeclaration>(withSelf = true)
|
||||
.map { it.name ?: "<no name>" }
|
||||
.toList()
|
||||
.asReversed()
|
||||
.joinToString(separator = ".")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDeclarationAtCaret(): KtDeclaration =
|
||||
file.findElementAt(myFixture.caretOffset)
|
||||
?.parentOfType()
|
||||
?: error("No KtDeclaration found at caret with position ${myFixture.caretOffset}")
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.frontend.api.components;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/idea-frontend-fir/testData/components/overridenDeclarations")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class OverriddenDeclarationProviderTestGenerated extends AbstractOverriddenDeclarationProviderTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInOverridenDeclarations() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/testData/components/overridenDeclarations"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("inOtherFile.kt")
|
||||
public void testInOtherFile() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/components/overridenDeclarations/inOtherFile.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("intersectionOverride.kt")
|
||||
public void testIntersectionOverride() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/components/overridenDeclarations/intersectionOverride.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("intersectionOverride2.kt")
|
||||
public void testIntersectionOverride2() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/components/overridenDeclarations/intersectionOverride2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("javaAccessors.kt")
|
||||
public void testJavaAccessors() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/components/overridenDeclarations/javaAccessors.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multipleInterfaces.kt")
|
||||
public void testMultipleInterfaces() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/components/overridenDeclarations/multipleInterfaces.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("sequenceOfOverrides.kt")
|
||||
public void testSequenceOfOverrides() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/components/overridenDeclarations/sequenceOfOverrides.kt");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user