[LL FIR] merge DeclarationBaseStructureElement into FileStructureElement
To simplify the implementation
This commit is contained in:
committed by
Space Team
parent
a9f7c153d2
commit
3f31bdb877
+75
-52
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
|
|||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirModuleResolveComponents
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirModuleResolveComponents
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.ClassDiagnosticRetriever
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.ClassDiagnosticRetriever
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.FileDiagnosticRetriever
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.FileDiagnosticRetriever
|
||||||
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.FileStructureElementDiagnosticRetriever
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.FileStructureElementDiagnostics
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.FileStructureElementDiagnostics
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.ScriptDiagnosticRetriever
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.ScriptDiagnosticRetriever
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.SingleNonLocalDeclarationDiagnosticRetriever
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.SingleNonLocalDeclarationDiagnosticRetriever
|
||||||
@@ -19,15 +20,42 @@ import org.jetbrains.kotlin.fir.FirElement
|
|||||||
import org.jetbrains.kotlin.fir.correspondingProperty
|
import org.jetbrains.kotlin.fir.correspondingProperty
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor
|
import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor
|
||||||
|
import org.jetbrains.kotlin.fir.utils.exceptions.withFirEntry
|
||||||
import org.jetbrains.kotlin.fir.visitors.FirVisitor
|
import org.jetbrains.kotlin.fir.visitors.FirVisitor
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.utils.exceptions.requireWithAttachment
|
||||||
|
|
||||||
internal sealed class FileStructureElement {
|
internal sealed class FileStructureElement(
|
||||||
abstract val mappings: KtToFirMapping
|
val declaration: FirDeclaration,
|
||||||
abstract val diagnostics: FileStructureElementDiagnostics
|
firFile: FirFile,
|
||||||
|
retriever: FileStructureElementDiagnosticRetriever,
|
||||||
|
moduleComponents: LLFirModuleResolveComponents,
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
val actualResolvePhase = declaration.resolvePhase
|
||||||
|
requireWithAttachment(
|
||||||
|
actualResolvePhase == FirResolvePhase.BODY_RESOLVE,
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
${this::class.simpleName} can be created only for fully resolved declaration.
|
||||||
|
Actual phase: $actualResolvePhase
|
||||||
|
""".trimIndent()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
withFirEntry("declaration", declaration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val mappings: KtToFirMapping = KtToFirMapping(declaration)
|
||||||
|
|
||||||
|
val diagnostics: FileStructureElementDiagnostics = FileStructureElementDiagnostics(
|
||||||
|
firFile,
|
||||||
|
retriever,
|
||||||
|
moduleComponents,
|
||||||
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun recorderFor(fir: FirElement): FirElementsRecorder = when (fir) {
|
fun recorderFor(fir: FirDeclaration): FirElementsRecorder = when (fir) {
|
||||||
is FirFile -> RootStructureElement.Recorder
|
is FirFile -> RootStructureElement.Recorder
|
||||||
is FirScript -> RootScriptStructureElement.Recorder
|
is FirScript -> RootScriptStructureElement.Recorder
|
||||||
is FirRegularClass -> ClassDeclarationStructureElement.Recorder(fir)
|
is FirRegularClass -> ClassDeclarationStructureElement.Recorder(fir)
|
||||||
@@ -36,7 +64,7 @@ internal sealed class FileStructureElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class KtToFirMapping(firElement: FirElement) {
|
internal class KtToFirMapping(firElement: FirDeclaration) {
|
||||||
private val mapping = FirElementsRecorder.recordElementsFrom(
|
private val mapping = FirElementsRecorder.recordElementsFrom(
|
||||||
firElement = firElement,
|
firElement = firElement,
|
||||||
recorder = FileStructureElement.recorderFor(firElement),
|
recorder = FileStructureElement.recorderFor(firElement),
|
||||||
@@ -55,52 +83,52 @@ internal class KtToFirMapping(firElement: FirElement) {
|
|||||||
current is KtDotQualifiedExpression ||
|
current is KtDotQualifiedExpression ||
|
||||||
current is KtNullableType
|
current is KtNullableType
|
||||||
) {
|
) {
|
||||||
// We are still referring to the same element with possible type parameter/name qualification/nullability, hence it is always
|
// We are still referring to the same element with possible type parameter/name qualification/nullability,
|
||||||
// sane to return corresponding element if present
|
// hence it is always correct to return a corresponding element if present
|
||||||
if (current is KtElement) getElement(current)?.let { return it }
|
if (current is KtElement) getElement(current)?.let { return it }
|
||||||
current = current.parent
|
current = current.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here current is the lowest ancestor that has different corresponding text
|
// Here current is the lowest ancestor that has different corresponding text
|
||||||
return when (current) {
|
return when (current) {
|
||||||
// Constants with unary operation (i.e. +1 or -1) are saved as leaf element of FIR tree
|
// Constants with unary operation (i.e., +1 or -1) are saved as a leaf element of FIR tree
|
||||||
is KtPrefixExpression,
|
is KtPrefixExpression,
|
||||||
// There is no separate element for annotation construction call
|
// There is no separate element for annotation construction call
|
||||||
is KtAnnotationEntry,
|
is KtAnnotationEntry,
|
||||||
// We replace source for selector for that of whole expression
|
// We replace a source for selector with the whole expression
|
||||||
is KtSafeQualifiedExpression,
|
is KtSafeQualifiedExpression,
|
||||||
// Top level destructuring declarations does not have FIR for r-value at the moment, would probably be changed later
|
// Top level destructuring declarations do not have FIR for r-value at the moment, would probably be changed later
|
||||||
is KtDestructuringDeclaration,
|
is KtDestructuringDeclaration,
|
||||||
// There is no separate FIR node for this in this@foo expressions, same for super@Foo
|
// There is no separate FIR node for this in this@foo expressions, same for super@Foo
|
||||||
is KtThisExpression,
|
is KtThisExpression,
|
||||||
is KtSuperExpression,
|
is KtSuperExpression,
|
||||||
// Part of path in import/package directives has no FIR node
|
// Part of the path in import/package directives has no FIR node
|
||||||
is KtImportDirective,
|
is KtImportDirective,
|
||||||
is KtPackageDirective,
|
is KtPackageDirective,
|
||||||
// Super type refs are not recorded
|
// Super type refs are not recorded
|
||||||
is KtSuperTypeCallEntry,
|
is KtSuperTypeCallEntry,
|
||||||
// this/super in delegation calls are not part of FIR tree, this(args) is
|
// this/super in delegation calls are not part of FIR tree, this(args) is
|
||||||
is KtConstructorDelegationCall,
|
is KtConstructorDelegationCall,
|
||||||
// In case of type projection we are not recording corresponding type reference
|
// In case of type projection we are not recording the corresponding type reference
|
||||||
is KtTypeProjection,
|
is KtTypeProjection,
|
||||||
// If we have, say, A(), reference A is not recorded, while call A() is recorded
|
// If we have, say, A(), reference A is not recorded, while call A() is recorded
|
||||||
is KtCallExpression ->
|
is KtCallExpression,
|
||||||
getElement(current as KtElement)
|
-> getElement(current as KtElement)
|
||||||
is KtBinaryExpression ->
|
is KtBinaryExpression ->
|
||||||
// Here there is no separate FIR node for partial operator calls (like for a[i] = 1, there is no separate node for a[i])
|
// Here there is no separate FIR node for partial operator calls (like for a[i] = 1, there is no separate node for a[i])
|
||||||
if (element is KtArrayAccessExpression || element is KtOperationReferenceExpression) getElement(current) else null
|
if (element is KtArrayAccessExpression || element is KtOperationReferenceExpression) getElement(current) else null
|
||||||
is KtBlockExpression ->
|
is KtBlockExpression ->
|
||||||
// For script initializers we need to return FIR element for script itself
|
// For script initializers, we need to return FIR element for script itself
|
||||||
if (element is KtScriptInitializer) getElement(current.parent as KtScript) else null
|
if (element is KtScriptInitializer) getElement(current.parent as KtScript) else null
|
||||||
is PsiErrorElement -> {
|
is PsiErrorElement -> {
|
||||||
val parent = current.parent
|
val parent = current.parent
|
||||||
if (parent is KtDestructuringDeclaration) getElement(parent) else null
|
if (parent is KtDestructuringDeclaration) getElement(parent) else null
|
||||||
}
|
}
|
||||||
// Value argument names and corresponding references are not part of FIR tree
|
// Value argument names and corresponding references are not part of the FIR tree
|
||||||
is KtValueArgumentName -> getElement(current.parent as KtValueArgument)
|
is KtValueArgumentName -> getElement(current.parent as KtValueArgument)
|
||||||
is KtContainerNode -> {
|
is KtContainerNode -> {
|
||||||
val parent = current.parent
|
val parent = current.parent
|
||||||
// Labels in labeled expression (i.e. return@foo) has no FIR node
|
// Labels in labeled expression (i.e., return@foo) have no FIR node
|
||||||
if (parent is KtExpressionWithLabel) getElement(parent) else null
|
if (parent is KtExpressionWithLabel) getElement(parent) else null
|
||||||
}
|
}
|
||||||
// Enum entries/annotation entries constructor calls
|
// Enum entries/annotation entries constructor calls
|
||||||
@@ -112,21 +140,16 @@ internal class KtToFirMapping(firElement: FirElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class DeclarationBaseStructureElement<F : FirDeclaration>(val declaration: F) : FileStructureElement() {
|
|
||||||
override val mappings: KtToFirMapping = KtToFirMapping(declaration)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class RootScriptStructureElement(
|
internal class RootScriptStructureElement(
|
||||||
file: FirFile,
|
file: FirFile,
|
||||||
script: FirScript,
|
script: FirScript,
|
||||||
moduleComponents: LLFirModuleResolveComponents,
|
moduleComponents: LLFirModuleResolveComponents,
|
||||||
) : DeclarationBaseStructureElement<FirScript>(script) {
|
) : FileStructureElement(
|
||||||
override val diagnostics: FileStructureElementDiagnostics = FileStructureElementDiagnostics(
|
declaration = script,
|
||||||
file,
|
firFile = file,
|
||||||
ScriptDiagnosticRetriever(declaration),
|
retriever = ScriptDiagnosticRetriever(script),
|
||||||
moduleComponents,
|
moduleComponents = moduleComponents,
|
||||||
)
|
) {
|
||||||
|
|
||||||
object Recorder : FirElementsRecorder() {
|
object Recorder : FirElementsRecorder() {
|
||||||
override fun visitScript(script: FirScript, data: MutableMap<KtElement, FirElement>) {
|
override fun visitScript(script: FirScript, data: MutableMap<KtElement, FirElement>) {
|
||||||
cacheElement(script, data)
|
cacheElement(script, data)
|
||||||
@@ -144,13 +167,12 @@ internal class ClassDeclarationStructureElement(
|
|||||||
file: FirFile,
|
file: FirFile,
|
||||||
clazz: FirRegularClass,
|
clazz: FirRegularClass,
|
||||||
moduleComponents: LLFirModuleResolveComponents,
|
moduleComponents: LLFirModuleResolveComponents,
|
||||||
) : DeclarationBaseStructureElement<FirRegularClass>(clazz) {
|
) : FileStructureElement(
|
||||||
override val diagnostics = FileStructureElementDiagnostics(
|
declaration = clazz,
|
||||||
file,
|
firFile = file,
|
||||||
ClassDiagnosticRetriever(declaration),
|
retriever = ClassDiagnosticRetriever(clazz),
|
||||||
moduleComponents,
|
moduleComponents = moduleComponents
|
||||||
)
|
) {
|
||||||
|
|
||||||
class Recorder(private val firClass: FirRegularClass) : FirElementsRecorder() {
|
class Recorder(private val firClass: FirRegularClass) : FirElementsRecorder() {
|
||||||
override fun visitProperty(property: FirProperty, data: MutableMap<KtElement, FirElement>) {
|
override fun visitProperty(property: FirProperty, data: MutableMap<KtElement, FirElement>) {
|
||||||
}
|
}
|
||||||
@@ -193,13 +215,12 @@ internal class DeclarationStructureElement(
|
|||||||
file: FirFile,
|
file: FirFile,
|
||||||
declaration: FirDeclaration,
|
declaration: FirDeclaration,
|
||||||
moduleComponents: LLFirModuleResolveComponents,
|
moduleComponents: LLFirModuleResolveComponents,
|
||||||
) : DeclarationBaseStructureElement<FirDeclaration>(declaration) {
|
) : FileStructureElement(
|
||||||
override val diagnostics = FileStructureElementDiagnostics(
|
declaration = declaration,
|
||||||
file,
|
firFile = file,
|
||||||
SingleNonLocalDeclarationDiagnosticRetriever(declaration),
|
retriever = SingleNonLocalDeclarationDiagnosticRetriever(declaration),
|
||||||
moduleComponents,
|
moduleComponents = moduleComponents,
|
||||||
)
|
) {
|
||||||
|
|
||||||
object Recorder : FirElementsRecorder() {
|
object Recorder : FirElementsRecorder() {
|
||||||
override fun visitConstructor(constructor: FirConstructor, data: MutableMap<KtElement, FirElement>) {
|
override fun visitConstructor(constructor: FirConstructor, data: MutableMap<KtElement, FirElement>) {
|
||||||
super.visitConstructor(constructor, data)
|
super.visitConstructor(constructor, data)
|
||||||
@@ -216,12 +237,14 @@ internal class DeclarationStructureElement(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class RootStructureElement(
|
internal class RootStructureElement(
|
||||||
val file: FirFile,
|
file: FirFile,
|
||||||
moduleComponents: LLFirModuleResolveComponents,
|
moduleComponents: LLFirModuleResolveComponents,
|
||||||
) : FileStructureElement() {
|
) : FileStructureElement(
|
||||||
override val mappings = KtToFirMapping(file)
|
declaration = file,
|
||||||
override val diagnostics = FileStructureElementDiagnostics(file, FileDiagnosticRetriever, moduleComponents)
|
firFile = file,
|
||||||
|
retriever = FileDiagnosticRetriever,
|
||||||
|
moduleComponents = moduleComponents
|
||||||
|
) {
|
||||||
object Recorder : FirElementsRecorder() {
|
object Recorder : FirElementsRecorder() {
|
||||||
override fun visitElement(element: FirElement, data: MutableMap<KtElement, FirElement>) {
|
override fun visitElement(element: FirElement, data: MutableMap<KtElement, FirElement>) {
|
||||||
if (element !is FirDeclaration || element is FirFile) {
|
if (element !is FirDeclaration || element is FirFile) {
|
||||||
|
|||||||
+1
-1
@@ -56,7 +56,7 @@ abstract class AbstractFirContextCollectionTest : AbstractAnalysisApiBasedTest()
|
|||||||
val fileStructure = fileStructureCache.getFileStructure(mainFile)
|
val fileStructure = fileStructureCache.getFileStructure(mainFile)
|
||||||
val allStructureElements = fileStructure.getAllStructureElements()
|
val allStructureElements = fileStructure.getAllStructureElements()
|
||||||
|
|
||||||
handler.elementsToCheckContext = allStructureElements.map(FileStructureElement::firDeclaration)
|
handler.elementsToCheckContext = allStructureElements.map(FileStructureElement::declaration)
|
||||||
handler.firFile = mainFile.getOrBuildFirFile(firResolveSession)
|
handler.firFile = mainFile.getOrBuildFirFile(firResolveSession)
|
||||||
|
|
||||||
mainFile.getDiagnostics(firResolveSession, DiagnosticCheckerFilter.ONLY_COMMON_CHECKERS)
|
mainFile.getDiagnostics(firResolveSession, DiagnosticCheckerFilter.ONLY_COMMON_CHECKERS)
|
||||||
|
|||||||
+1
-8
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators.Analys
|
|||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators.AnalysisApiFirSourceTestConfigurator
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators.AnalysisApiFirSourceTestConfigurator
|
||||||
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
||||||
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedTest
|
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedTest
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
|
||||||
import org.jetbrains.kotlin.fir.psi
|
import org.jetbrains.kotlin.fir.psi
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||||
@@ -32,7 +31,7 @@ abstract class AbstractFileStructureTest : AbstractAnalysisApiBasedTest() {
|
|||||||
override fun doTestByMainFile(mainFile: KtFile, mainModule: TestModule, testServices: TestServices) {
|
override fun doTestByMainFile(mainFile: KtFile, mainModule: TestModule, testServices: TestServices) {
|
||||||
val fileStructure = mainFile.getFileStructure()
|
val fileStructure = mainFile.getFileStructure()
|
||||||
val allStructureElements = fileStructure.getAllStructureElements(mainFile)
|
val allStructureElements = fileStructure.getAllStructureElements(mainFile)
|
||||||
val declarationToStructureElement = allStructureElements.associateBy { it.firDeclaration.psi }
|
val declarationToStructureElement = allStructureElements.associateBy { it.declaration.psi }
|
||||||
val elementToComment = mutableMapOf<PsiElement, String>()
|
val elementToComment = mutableMapOf<PsiElement, String>()
|
||||||
mainFile.forEachDescendantOfType<KtDeclaration> { ktDeclaration ->
|
mainFile.forEachDescendantOfType<KtDeclaration> { ktDeclaration ->
|
||||||
val structureElement = declarationToStructureElement[ktDeclaration] ?: return@forEachDescendantOfType
|
val structureElement = declarationToStructureElement[ktDeclaration] ?: return@forEachDescendantOfType
|
||||||
@@ -128,12 +127,6 @@ abstract class AbstractFileStructureTest : AbstractAnalysisApiBasedTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val FileStructureElement.firDeclaration: FirDeclaration
|
|
||||||
get() = when (this) {
|
|
||||||
is RootStructureElement -> file
|
|
||||||
is DeclarationBaseStructureElement<*> -> declaration
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class AbstractSourceFileStructureTest : AbstractFileStructureTest() {
|
abstract class AbstractSourceFileStructureTest : AbstractFileStructureTest() {
|
||||||
override val configurator = AnalysisApiFirSourceTestConfigurator(analyseInDependentSession = false)
|
override val configurator = AnalysisApiFirSourceTestConfigurator(analyseInDependentSession = false)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user