FIR IDE: introduce symbol pointers for restoring symbols in another read action
fix pointer
This commit is contained in:
@@ -89,6 +89,7 @@ import org.jetbrains.kotlin.idea.fir.AbstractKtDeclarationAndFirDeclarationEqual
|
||||
import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.AbstractResolveCallTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.AbstractStdlibSymbolsBuildingTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.AbstractSymbolPointerTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.AbstractSymbolsByPsiBuildingTest
|
||||
import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyTest
|
||||
import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyWithLibTest
|
||||
@@ -952,6 +953,10 @@ fun main(args: Array<String>) {
|
||||
testClass<AbstractStdlibSymbolsBuildingTest> {
|
||||
model("stdLibSymbols", extension = "txt")
|
||||
}
|
||||
|
||||
testClass<AbstractSymbolPointerTest> {
|
||||
model("symbolPointer", extension = "kt")
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("idea/idea-frontend-fir/idea-fir-low-level-api/tests", "idea/testData") {
|
||||
|
||||
+2
@@ -5,6 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.frontend.api
|
||||
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
|
||||
data class ImplicitReceiverSmartCast(val types: Collection<KtType>, val kind: ImplicitReceiverSmartcastKind)
|
||||
|
||||
enum class ImplicitReceiverSmartcastKind {
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.frontend.api
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.frontend.api
|
||||
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
|
||||
sealed class KtTypeArgument
|
||||
|
||||
object KtStarProjectionTypeArgument : KtTypeArgument()
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.frontend.api.symbols
|
||||
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
+2
@@ -18,6 +18,8 @@ abstract class KtTypeAliasSymbol : KtClassLikeSymbol() {
|
||||
|
||||
abstract class KtClassOrObjectSymbol : KtClassLikeSymbol(), KtSymbolWithTypeParameters, KtSymbolWithModality<KtSymbolModality> {
|
||||
abstract val classKind: KtClassKind
|
||||
|
||||
abstract override fun createPointer(): KtSymbolPointer<KtClassOrObjectSymbol>
|
||||
}
|
||||
|
||||
enum class KtClassKind {
|
||||
|
||||
+2
@@ -32,4 +32,6 @@ abstract class KtConstructorSymbol : KtFunctionLikeSymbol() {
|
||||
abstract override val valueParameters: List<KtConstructorParameterSymbol>
|
||||
abstract val isPrimary: Boolean
|
||||
abstract val owner: KtClassOrObjectSymbol
|
||||
|
||||
abstract override fun createPointer(): KtSymbolPointer<KtConstructorSymbol>
|
||||
}
|
||||
+2
@@ -9,4 +9,6 @@ import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
abstract class KtPackageSymbol : KtSymbol {
|
||||
abstract val fqName: FqName
|
||||
|
||||
abstract override fun createPointer(): KtSymbolPointer<KtPackageSymbol>
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import org.jetbrains.kotlin.idea.frontend.api.ValidityOwner
|
||||
interface KtSymbol : ValidityOwner {
|
||||
val origin: KtSymbolOrigin
|
||||
val psi: PsiElement?
|
||||
|
||||
fun createPointer(): KtSymbolPointer<KtSymbol> = NonRestorableKtSymbolPointer
|
||||
}
|
||||
|
||||
enum class KtSymbolOrigin {
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.symbols
|
||||
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
|
||||
/**
|
||||
* `KtSymbol` is valid only during read action it was created in
|
||||
* To pass the symbol from one read action to another the KtSymbolPointer should be used
|
||||
*
|
||||
* We can restore the symbol
|
||||
* * for function & property symbol if its signature was not changed
|
||||
* * for local variable symbol if code block it was declared in was not changed
|
||||
* * for class & type alias symbols if its qualified name was not changed
|
||||
* * for package symbol if the package is still exists
|
||||
*
|
||||
* @see org.jetbrains.kotlin.idea.frontend.api.ReadActionConfinementValidityToken
|
||||
*/
|
||||
interface KtSymbolPointer<out S : KtSymbol> {
|
||||
/**
|
||||
* @return restored symbol (possibly the new symbol instance) if one is still valid, `null` otherwise
|
||||
*/
|
||||
fun restoreSymbol(analysisSession: KtAnalysisSession): S?
|
||||
}
|
||||
|
||||
object NonRestorableKtSymbolPointer : KtSymbolPointer<Nothing> {
|
||||
override fun restoreSymbol(analysisSession: KtAnalysisSession): Nothing? = null
|
||||
}
|
||||
|
||||
inline fun <S : KtSymbol> symbolPointer(crossinline getSymbol: (KtAnalysisSession) -> S?) = object : KtSymbolPointer<S> {
|
||||
override fun restoreSymbol(analysisSession: KtAnalysisSession): S? = getSymbol(analysisSession)
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.frontend.api.symbols
|
||||
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
interface KtNamedSymbol : KtSymbol {
|
||||
|
||||
+3
-1
@@ -3,8 +3,10 @@
|
||||
* 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
|
||||
package org.jetbrains.kotlin.idea.frontend.api.types
|
||||
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtTypeArgument
|
||||
import org.jetbrains.kotlin.idea.frontend.api.ValidityOwner
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassLikeSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
+2
@@ -31,4 +31,6 @@ internal class KtFirAnonymousFunctionSymbol(
|
||||
builder.buildParameterSymbol(valueParameter)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createPointer(): KtSymbolPointer<KtAnonymousFunctionSymbol> = NonRestorableKtSymbolPointer
|
||||
}
|
||||
+6
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.idea.fir.findPsi
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.ValidityOwner
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtSymbolByFirBuilder
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.ReadOnlyWeakRef
|
||||
@@ -57,4 +58,9 @@ internal class KtFirClassOrObjectSymbol(
|
||||
else -> KtSymbolKind.TOP_LEVEL
|
||||
}
|
||||
}
|
||||
|
||||
override fun createPointer(): KtSymbolPointer<KtClassOrObjectSymbol> {
|
||||
val classId = classId
|
||||
return symbolPointer { session -> session.symbolProvider.getClassOrObjectSymbolByClassId(classId) }
|
||||
}
|
||||
}
|
||||
+16
-4
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.symbols
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.fir.declarations.FirConstructor
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.getPrimaryConstructorIfAny
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirValueParameterImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
|
||||
import org.jetbrains.kotlin.idea.fir.findPsi
|
||||
@@ -16,10 +17,7 @@ import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtSymbolByFirBuilder
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.cached
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.weakRef
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtConstructorParameterSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtConstructorSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbolKind
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.withValidityAssertion
|
||||
|
||||
internal class KtFirConstructorSymbol(
|
||||
@@ -49,4 +47,18 @@ internal class KtFirConstructorSymbol(
|
||||
check(firClass is FirRegularClass) { "Owner class for constructor should be FirRegularClass, but ${firClass::class} was met" }
|
||||
builder.buildClassSymbol(firClass)
|
||||
}
|
||||
|
||||
override fun createPointer(): KtSymbolPointer<KtConstructorSymbol> = withValidityAssertion {
|
||||
if (!isPrimary) {
|
||||
// TODO for now we can not find symbol for member function :(
|
||||
return NonRestorableKtSymbolPointer
|
||||
}
|
||||
val ownerClassId = owner.classId
|
||||
return symbolPointer { session ->
|
||||
val ownerSymbol = session.symbolProvider.getClassOrObjectSymbolByClassId(ownerClassId) ?: return@symbolPointer null
|
||||
check(ownerSymbol is KtFirSymbol<*>)
|
||||
val classFir = (ownerSymbol.fir as? FirRegularClass) ?: error("FirRegularClass expected but ${ownerSymbol.fir::class} found")
|
||||
classFir.getPrimaryConstructorIfAny()?.let(builder::buildConstructorSymbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -13,12 +13,10 @@ import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.idea.frontend.api.ValidityOwner
|
||||
import org.jetbrains.kotlin.idea.frontend.api.ValidityOwnerByValidityToken
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtSymbolByFirBuilder
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.cached
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtPackageSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbolOrigin
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbolPointer
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.symbolPointer
|
||||
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
class KtFirPackageSymbol(
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
class A {
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
class A() {
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.symbols
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.addExternalTestFiles
|
||||
import org.jetbrains.kotlin.idea.executeOnPooledThreadInReadAction
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.junit.Assert
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractSymbolPointerTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
protected fun doTest(path: String) {
|
||||
val file = File(path)
|
||||
val ktFile = myFixture.configureByText(file.name, FileUtil.loadFile(file)) as KtFile
|
||||
|
||||
val pointers = executeOnPooledThreadInReadAction {
|
||||
val analysisSession = KtFirAnalysisSession(ktFile)
|
||||
val declarationSymbols = ktFile.collectDescendantsOfType<KtDeclaration>().map { declaration ->
|
||||
analysisSession.symbolProvider.getSymbol(declaration)
|
||||
}
|
||||
declarationSymbols.map { PointerWithPsi(it.createPointer(), it.psi!!) }
|
||||
}
|
||||
|
||||
// another read action
|
||||
executeOnPooledThreadInReadAction {
|
||||
val analysisSession = KtFirAnalysisSession(ktFile)
|
||||
pointers.forEach { (pointer, psi) ->
|
||||
val restored = pointer.restoreSymbol(analysisSession) ?: error("Symbol $psi was not not restored correctly")
|
||||
Assert.assertEquals(restored.psi!!, psi)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class PointerWithPsi(val pointer: KtSymbolPointer<*>, val psi: PsiElement)
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.symbols;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
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/symbolPointer")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class SymbolPointerTestGenerated extends AbstractSymbolPointerTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInSymbolPointer() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/testData/symbolPointer"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("class.kt")
|
||||
public void testClass() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/symbolPointer/class.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("classPrimaryConstructor.kt")
|
||||
public void testClassPrimaryConstructor() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/symbolPointer/classPrimaryConstructor.kt");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user