[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.diagnostics.ClassDiagnosticRetriever
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.ScriptDiagnosticRetriever
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.declarations.*
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.psi.*
import org.jetbrains.kotlin.utils.exceptions.requireWithAttachment
internal sealed class FileStructureElement {
abstract val mappings: KtToFirMapping
abstract val diagnostics: FileStructureElementDiagnostics
internal sealed class FileStructureElement(
val declaration: FirDeclaration,
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 {
fun recorderFor(fir: FirElement): FirElementsRecorder = when (fir) {
fun recorderFor(fir: FirDeclaration): FirElementsRecorder = when (fir) {
is FirFile -> RootStructureElement.Recorder
is FirScript -> RootScriptStructureElement.Recorder
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(
firElement = firElement,
recorder = FileStructureElement.recorderFor(firElement),
@@ -55,52 +83,52 @@ internal class KtToFirMapping(firElement: FirElement) {
current is KtDotQualifiedExpression ||
current is KtNullableType
) {
// We are still referring to the same element with possible type parameter/name qualification/nullability, hence it is always
// sane to return corresponding element if present
// We are still referring to the same element with possible type parameter/name qualification/nullability,
// hence it is always correct to return a corresponding element if present
if (current is KtElement) getElement(current)?.let { return it }
current = current.parent
}
// Here current is the lowest ancestor that has different corresponding text
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,
// There is no separate element for annotation construction call
// There is no separate element for annotation construction call
is KtAnnotationEntry,
// We replace source for selector for that of whole expression
// We replace a source for selector with the whole expression
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,
// 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 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 KtPackageDirective,
// Super type refs are not recorded
// Super type refs are not recorded
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,
// 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,
// If we have, say, A(), reference A is not recorded, while call A() is recorded
is KtCallExpression ->
getElement(current as KtElement)
// If we have, say, A(), reference A is not recorded, while call A() is recorded
is KtCallExpression,
-> getElement(current as KtElement)
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])
if (element is KtArrayAccessExpression || element is KtOperationReferenceExpression) getElement(current) else null
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
is PsiErrorElement -> {
val parent = current.parent
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 KtContainerNode -> {
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
}
// 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(
file: FirFile,
script: FirScript,
moduleComponents: LLFirModuleResolveComponents,
) : DeclarationBaseStructureElement<FirScript>(script) {
override val diagnostics: FileStructureElementDiagnostics = FileStructureElementDiagnostics(
file,
ScriptDiagnosticRetriever(declaration),
moduleComponents,
)
) : FileStructureElement(
declaration = script,
firFile = file,
retriever = ScriptDiagnosticRetriever(script),
moduleComponents = moduleComponents,
) {
object Recorder : FirElementsRecorder() {
override fun visitScript(script: FirScript, data: MutableMap<KtElement, FirElement>) {
cacheElement(script, data)
@@ -144,13 +167,12 @@ internal class ClassDeclarationStructureElement(
file: FirFile,
clazz: FirRegularClass,
moduleComponents: LLFirModuleResolveComponents,
) : DeclarationBaseStructureElement<FirRegularClass>(clazz) {
override val diagnostics = FileStructureElementDiagnostics(
file,
ClassDiagnosticRetriever(declaration),
moduleComponents,
)
) : FileStructureElement(
declaration = clazz,
firFile = file,
retriever = ClassDiagnosticRetriever(clazz),
moduleComponents = moduleComponents
) {
class Recorder(private val firClass: FirRegularClass) : FirElementsRecorder() {
override fun visitProperty(property: FirProperty, data: MutableMap<KtElement, FirElement>) {
}
@@ -193,13 +215,12 @@ internal class DeclarationStructureElement(
file: FirFile,
declaration: FirDeclaration,
moduleComponents: LLFirModuleResolveComponents,
) : DeclarationBaseStructureElement<FirDeclaration>(declaration) {
override val diagnostics = FileStructureElementDiagnostics(
file,
SingleNonLocalDeclarationDiagnosticRetriever(declaration),
moduleComponents,
)
) : FileStructureElement(
declaration = declaration,
firFile = file,
retriever = SingleNonLocalDeclarationDiagnosticRetriever(declaration),
moduleComponents = moduleComponents,
) {
object Recorder : FirElementsRecorder() {
override fun visitConstructor(constructor: FirConstructor, data: MutableMap<KtElement, FirElement>) {
super.visitConstructor(constructor, data)
@@ -216,12 +237,14 @@ internal class DeclarationStructureElement(
}
internal class RootStructureElement(
val file: FirFile,
file: FirFile,
moduleComponents: LLFirModuleResolveComponents,
) : FileStructureElement() {
override val mappings = KtToFirMapping(file)
override val diagnostics = FileStructureElementDiagnostics(file, FileDiagnosticRetriever, moduleComponents)
) : FileStructureElement(
declaration = file,
firFile = file,
retriever = FileDiagnosticRetriever,
moduleComponents = moduleComponents
) {
object Recorder : FirElementsRecorder() {
override fun visitElement(element: FirElement, data: MutableMap<KtElement, FirElement>) {
if (element !is FirDeclaration || element is FirFile) {
@@ -56,7 +56,7 @@ abstract class AbstractFirContextCollectionTest : AbstractAnalysisApiBasedTest()
val fileStructure = fileStructureCache.getFileStructure(mainFile)
val allStructureElements = fileStructure.getAllStructureElements()
handler.elementsToCheckContext = allStructureElements.map(FileStructureElement::firDeclaration)
handler.elementsToCheckContext = allStructureElements.map(FileStructureElement::declaration)
handler.firFile = mainFile.getOrBuildFirFile(firResolveSession)
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.project.structure.ProjectStructureProvider
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.psi.*
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
@@ -32,7 +31,7 @@ abstract class AbstractFileStructureTest : AbstractAnalysisApiBasedTest() {
override fun doTestByMainFile(mainFile: KtFile, mainModule: TestModule, testServices: TestServices) {
val fileStructure = mainFile.getFileStructure()
val allStructureElements = fileStructure.getAllStructureElements(mainFile)
val declarationToStructureElement = allStructureElements.associateBy { it.firDeclaration.psi }
val declarationToStructureElement = allStructureElements.associateBy { it.declaration.psi }
val elementToComment = mutableMapOf<PsiElement, String>()
mainFile.forEachDescendantOfType<KtDeclaration> { ktDeclaration ->
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() {
override val configurator = AnalysisApiFirSourceTestConfigurator(analyseInDependentSession = false)
}