[LL FIR] merge DeclarationBaseStructureElement into FileStructureElement

To simplify the implementation
This commit is contained in:
Dmitrii Gridin
2024-02-16 23:36:09 +01:00
committed by Space Team
parent a9f7c153d2
commit 3f31bdb877
3 changed files with 77 additions and 61 deletions
@@ -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) {
@@ -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)
@@ -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)
} }