[LL API] Support code fragment analysis

This commit is contained in:
Yan Zhulanow
2023-06-14 21:57:57 +09:00
committed by Space Team
parent 3fb038ffd0
commit 02af189066
32 changed files with 493 additions and 32 deletions
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2023 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.analysis.api.fir.test.cases.generated.cases.components.diagnosticProvider;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.analysis.api.fir.test.configurators.AnalysisApiFirTestConfiguratorFactory;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfiguratorFactoryData;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModuleKind;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisSessionMode;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiMode;
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.diagnosticProvider.AbstractCodeFragmentCollectDiagnosticsTest;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.analysis.api.GenerateAnalysisApiTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics")
@TestDataPath("$PROJECT_ROOT")
public class FirIdeNormalAnalysisSourceModuleCodeFragmentCollectDiagnosticsTestGenerated extends AbstractCodeFragmentCollectDiagnosticsTest {
@NotNull
@Override
public AnalysisApiTestConfigurator getConfigurator() {
return AnalysisApiFirTestConfiguratorFactory.INSTANCE.createConfigurator(
new AnalysisApiTestConfiguratorFactoryData(
FrontendKind.Fir,
TestModuleKind.Source,
AnalysisSessionMode.Normal,
AnalysisApiMode.Ide
)
);
}
@Test
public void testAllFilesPresentInCodeFragmentDiagnostics() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("localClass.kt")
public void testLocalClass() throws Exception {
runTest("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics/localClass.kt");
}
@Test
@TestMetadata("localFunction.kt")
public void testLocalFunction() throws Exception {
runTest("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics/localFunction.kt");
}
@Test
@TestMetadata("typeMismatch.kt")
public void testTypeMismatch() throws Exception {
runTest("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics/typeMismatch.kt");
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2023 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.analysis.api.impl.base.test.cases.components.diagnosticProvider
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import java.io.File
abstract class AbstractCodeFragmentCollectDiagnosticsTest : AbstractCollectDiagnosticsTest() {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val contextElement = testServices.expressionMarkerProvider.getElementOfTypeAtCaret<KtElement>(ktFile)
val fragmentText = module.files.single().originalFile
.run { File(parent, "$nameWithoutExtension.fragment.$extension") }
.readText()
val isBlockFragment = fragmentText.any { it == '\n' }
val project = ktFile.project
val factory = KtPsiFactory(project, markGenerated = false)
val codeFragment = when {
isBlockFragment -> factory.createBlockCodeFragment(fragmentText, contextElement)
else -> factory.createExpressionCodeFragment(fragmentText, contextElement)
}
super.doTestByFileStructure(codeFragment, module, testServices)
}
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2023 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.analysis.api.standalone.fir.test.cases.generated.cases.components.diagnosticProvider;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.analysis.api.standalone.fir.test.AnalysisApiFirStandaloneModeTestConfiguratorFactory;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfiguratorFactoryData;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModuleKind;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisSessionMode;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiMode;
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.diagnosticProvider.AbstractCodeFragmentCollectDiagnosticsTest;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.analysis.api.GenerateAnalysisApiTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics")
@TestDataPath("$PROJECT_ROOT")
public class FirStandaloneNormalAnalysisSourceModuleCodeFragmentCollectDiagnosticsTestGenerated extends AbstractCodeFragmentCollectDiagnosticsTest {
@NotNull
@Override
public AnalysisApiTestConfigurator getConfigurator() {
return AnalysisApiFirStandaloneModeTestConfiguratorFactory.INSTANCE.createConfigurator(
new AnalysisApiTestConfiguratorFactoryData(
FrontendKind.Fir,
TestModuleKind.Source,
AnalysisSessionMode.Normal,
AnalysisApiMode.Standalone
)
);
}
@Test
public void testAllFilesPresentInCodeFragmentDiagnostics() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("localClass.kt")
public void testLocalClass() throws Exception {
runTest("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics/localClass.kt");
}
@Test
@TestMetadata("localFunction.kt")
public void testLocalFunction() throws Exception {
runTest("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics/localFunction.kt");
}
@Test
@TestMetadata("typeMismatch.kt")
public void testTypeMismatch() throws Exception {
runTest("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics/typeMismatch.kt");
}
}
@@ -0,0 +1,6 @@
class Foo {
val a = x
val b = y
}
Foo().b
@@ -0,0 +1,4 @@
fun test() {
val x = 0
<caret>val y = 0
}
@@ -0,0 +1,4 @@
Diagnostics from elements:
for PSI element of type KtNameReferenceExpression at (3,13-14)
UNRESOLVED_REFERENCE text ranges: [(38,39)]
PSI: KtNameReferenceExpression at (3,13-14)
@@ -0,0 +1,5 @@
fun foo(): Pair<Int, Int> {
return x to y
}
foo()
@@ -0,0 +1,6 @@
// WITH_STDLIB
fun test() {
val x = 0
<caret>val y = 0
}
@@ -0,0 +1,4 @@
Diagnostics from elements:
for PSI element of type KtNameReferenceExpression at (2,17-18)
UNRESOLVED_REFERENCE text ranges: [(44,45)]
PSI: KtNameReferenceExpression at (2,17-18)
@@ -0,0 +1,6 @@
fun test() {
val x = 0
<caret>val y = 1
}
fun foo(a: String) {}
@@ -0,0 +1,4 @@
Diagnostics from elements:
for PSI element of type KtNameReferenceExpression at (1,5-6)
ARGUMENT_TYPE_MISMATCH text ranges: [(4,5)]
PSI: KtNameReferenceExpression at (1,5-6)
@@ -108,7 +108,7 @@ private fun collectDesignationPath(target: FirElementWithResolveState): List<Fir
return if (target.diagnostic == ConeDestructuringDeclarationsOnTopLevel) emptyList() else null return if (target.diagnostic == ConeDestructuringDeclarationsOnTopLevel) emptyList() else null
} }
is FirScript -> { is FirScript, is FirCodeFragment -> {
return emptyList() return emptyList()
} }
@@ -270,7 +270,7 @@ fun FirElementWithResolveState.tryCollectDesignation(): FirDesignation? =
fun FirElementWithResolveState.tryCollectDesignationWithFile(): FirDesignationWithFile? { fun FirElementWithResolveState.tryCollectDesignationWithFile(): FirDesignationWithFile? {
return when (this) { return when (this) {
is FirScript, is FirFileAnnotationsContainer -> { is FirScript, is FirCodeFragment, is FirFileAnnotationsContainer -> {
val firFile = getContainingFile() ?: return null val firFile = getContainingFile() ?: return null
FirDesignationWithFile(path = emptyList(), this, firFile) FirDesignationWithFile(path = emptyList(), this, firFile)
} }
@@ -5,13 +5,21 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.resolveToFirSymbol
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkCanceled import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkCanceled
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.analysis.utils.printer.parentsOfType
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContextForProvider import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContextForProvider
import org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor import org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor
import org.jetbrains.kotlin.fir.analysis.collectors.DiagnosticCollectorComponents import org.jetbrains.kotlin.fir.analysis.collectors.DiagnosticCollectorComponents
import org.jetbrains.kotlin.fir.declarations.FirCodeFragment
import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.psi
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtDeclaration
internal open class LLFirDiagnosticVisitor( internal open class LLFirDiagnosticVisitor(
context: CheckerContextForProvider, context: CheckerContextForProvider,
@@ -44,6 +52,34 @@ internal open class LLFirDiagnosticVisitor(
} }
} }
override fun visitCodeFragment(codeFragment: FirCodeFragment, data: Nothing?) {
val contextElement = (codeFragment.source.psi as? KtCodeFragment)?.context
if (contextElement != null) {
fun process(containingSymbols: List<FirDeclaration>) {
if (containingSymbols.isEmpty()) {
super.visitCodeFragment(codeFragment, data)
} else {
withDeclaration(containingSymbols.first()) {
process(containingSymbols.subList(1, containingSymbols.size))
}
}
}
val project = contextElement.project
val module = ProjectStructureProvider.getModule(project, contextElement, contextualModule = null)
val resolveSession = module.getFirResolveSession(project)
// Register containing declarations of a context element
contextElement.parentsOfType<KtDeclaration>().toList().asReversed()
.map { it.resolveToFirSymbol(resolveSession).fir }
.run(::process)
return
}
super.visitCodeFragment(codeFragment, data)
}
/** /**
* Some FirClassChecker may report diagnostics on class member headers. * Some FirClassChecker may report diagnostics on class member headers.
* That diagnostics should be suppressed if we have a `@Suppress` annotation on class member. * That diagnostics should be suppressed if we have a `@Suppress` annotation on class member.
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorV
import org.jetbrains.kotlin.fir.containingClass import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.classId import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.renderWithType
import org.jetbrains.kotlin.fir.resolve.SessionHolder import org.jetbrains.kotlin.fir.resolve.SessionHolder
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.utils.exceptions.withFirEntry import org.jetbrains.kotlin.fir.utils.exceptions.withFirEntry
@@ -67,7 +66,7 @@ internal object PersistenceContextCollector {
is FirCallableDeclaration -> declaration.symbol.callableId.isLocal is FirCallableDeclaration -> declaration.symbol.callableId.isLocal
is FirDanglingModifierList -> declaration.containingClass()?.classId?.isLocal == true is FirDanglingModifierList -> declaration.containingClass()?.classId?.isLocal == true
is FirAnonymousInitializer -> declaration.containingClass().classId.isLocal is FirAnonymousInitializer -> declaration.containingClass().classId.isLocal
is FirScript -> false is FirScript, is FirCodeFragment -> false
else -> errorWithAttachment("Unsupported declaration ${declaration::class}") { else -> errorWithAttachment("Unsupported declaration ${declaration::class}") {
withFirEntry("declaration", declaration) withFirEntry("declaration", declaration)
} }
@@ -262,7 +262,8 @@ internal fun PsiElement.getNonLocalContainingOrThisDeclaration(predicate: (KtDec
!notNullCandidate.isPartOf(parent) || !notNullCandidate.isPartOf(parent) ||
parent is KtClassInitializer || parent is KtClassInitializer ||
parent is KtObjectLiteralExpression || parent is KtObjectLiteralExpression ||
parent is KtCallElement parent is KtCallElement ||
parent is KtCodeFragment
) { ) {
// Candidate turned out to be local. Let's find another one. // Candidate turned out to be local. Let's find another one.
candidate = null candidate = null
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.utils.exceptions.checkWithAttachment
import org.jetbrains.kotlin.fir.builder.BodyBuildingMode import org.jetbrains.kotlin.fir.builder.BodyBuildingMode
import org.jetbrains.kotlin.fir.builder.PsiRawFirBuilder import org.jetbrains.kotlin.fir.builder.PsiRawFirBuilder
import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
/** /**
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isAncestor import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.canBePartOfParentDeclaration import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.canBePartOfParentDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.codeFragment
import org.jetbrains.kotlin.fir.correspondingProperty import org.jetbrains.kotlin.fir.correspondingProperty
import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
@@ -189,6 +190,13 @@ internal class FileStructure private constructor(
} }
private fun createStructureElement(container: KtElement): FileStructureElement = when { private fun createStructureElement(container: KtElement): FileStructureElement = when {
container is KtCodeFragment -> {
val firCodeFragment = firFile.codeFragment
firCodeFragment.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE)
val timestamp = container.modificationStamp
ReanalyzableCodeFragmentStructureElement(firFile, container, firCodeFragment.symbol, timestamp, moduleComponents)
}
container is KtFile -> { container is KtFile -> {
val firFile = moduleComponents.firFileBuilder.buildRawFirFileWithCaching(ktFile) val firFile = moduleComponents.firFileBuilder.buildRawFirFileWithCaching(ktFile)
firFile.lazyResolveToPhase(FirResolvePhase.IMPORTS) firFile.lazyResolveToPhase(FirResolvePhase.IMPORTS)
@@ -9,6 +9,9 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiErrorElement import com.intellij.psi.PsiErrorElement
import org.jetbrains.kotlin.KtFakeSourceElementKind 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.api.FirDesignation
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDesignation
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.FileStructureElementDiagnostics import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.FileStructureElementDiagnostics
@@ -20,7 +23,9 @@ 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.FirErrorConstructor import org.jetbrains.kotlin.fir.declarations.impl.FirErrorConstructor
import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor
import org.jetbrains.kotlin.fir.scopes.kotlinScopeProvider
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCodeFragmentSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
@@ -106,12 +111,12 @@ internal class KtToFirMapping(firElement: FirElement, recorder: FirElementsRecor
} }
} }
internal sealed class ReanalyzableStructureElement<KT : KtDeclaration, S : FirBasedSymbol<*>>( internal sealed class ReanalyzableStructureElement<KT : KtAnnotated, S : FirBasedSymbol<*>>(
firFile: FirFile, firFile: FirFile,
val firSymbol: S, val firSymbol: S,
moduleComponents: LLFirModuleResolveComponents, moduleComponents: LLFirModuleResolveComponents,
) : FileStructureElement(firFile, moduleComponents) { ) : FileStructureElement(firFile, moduleComponents) {
abstract override val psi: KtDeclaration abstract override val psi: KtAnnotated
abstract val timestamp: Long abstract val timestamp: Long
/** /**
@@ -132,6 +137,45 @@ internal sealed class ReanalyzableStructureElement<KT : KtDeclaration, S : FirBa
} }
} }
internal class ReanalyzableCodeFragmentStructureElement(
firFile: FirFile,
override val psi: KtCodeFragment,
firSymbol: FirCodeFragmentSymbol,
override val timestamp: Long,
moduleComponents: LLFirModuleResolveComponents,
) : ReanalyzableStructureElement<KtCodeFragment, FirCodeFragmentSymbol>(firFile, firSymbol, moduleComponents) {
override val mappings = KtToFirMapping(firSymbol.fir, recorder)
override fun reanalyze(): ReanalyzableStructureElement<KtCodeFragment, FirCodeFragmentSymbol> {
val originalCodeFragment = firSymbol.fir
val originalDesignation = originalCodeFragment.collectDesignation()
firFile.transformDeclarations(object : FirTransformer<Nothing?>() {
override fun <E : FirElement> transformElement(element: E, data: Nothing?): E = element
override fun transformCodeFragment(codeFragment: FirCodeFragment, data: Nothing?): FirCodeFragment {
return RawFirNonLocalDeclarationBuilder.build(
session = originalCodeFragment.moduleData.session,
scopeProvider = originalCodeFragment.moduleData.session.kotlinScopeProvider,
designation = originalDesignation,
rootNonLocalDeclaration = psi,
) as FirCodeFragment
}
}, null)
val newCodeFragment = firFile.codeFragment
newCodeFragment.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE)
return ReanalyzableCodeFragmentStructureElement(
firFile,
psi,
newCodeFragment.symbol,
psi.modificationStamp,
moduleComponents
)
}
}
internal class ReanalyzableFunctionStructureElement( internal class ReanalyzableFunctionStructureElement(
firFile: FirFile, firFile: FirFile,
override val psi: KtNamedFunction, override val psi: KtNamedFunction,
@@ -8,9 +8,10 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirInternals import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirInternals
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.resolveToFirSymbol import org.jetbrains.kotlin.analysis.low.level.api.fir.api.resolveToFirSymbol
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.codeFragment
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.errorWithFirSpecificEntries import org.jetbrains.kotlin.analysis.low.level.api.fir.util.errorWithFirSpecificEntries
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.fir.contracts.impl.FirEmptyContractDescription import org.jetbrains.kotlin.fir.contracts.impl.FirEmptyContractDescription
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
@@ -21,23 +22,44 @@ import org.jetbrains.kotlin.fir.expressions.FirLazyExpression
import org.jetbrains.kotlin.fir.expressions.builder.buildLazyBlock import org.jetbrains.kotlin.fir.expressions.builder.buildLazyBlock
import org.jetbrains.kotlin.fir.expressions.builder.buildLazyExpression import org.jetbrains.kotlin.fir.expressions.builder.buildLazyExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirContractCallBlock import org.jetbrains.kotlin.fir.expressions.impl.FirContractCallBlock
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
@Deprecated(
"Temporarily left for binary compatibility. Use invalidateAfterInBlockModification(KtElement) instead.",
replaceWith = ReplaceWith("invalidateAfterInBlockModification(declaration)", "org.jetbrains.kotlin.psi.KtElement"),
level = DeprecationLevel.HIDDEN,
)
@LLFirInternals
@Suppress("unused")
fun invalidateAfterInBlockModification(declaration: KtDeclaration): Boolean {
return invalidateAfterInBlockModification(declaration as KtElement)
}
/** /**
* Must be called in a write action. * Must be called in a write action.
* @return **false** if it is not in-block modification * @return **false** if it is not in-block modification
*/ */
@LLFirInternals @LLFirInternals
fun invalidateAfterInBlockModification(declaration: KtDeclaration): Boolean { fun invalidateAfterInBlockModification(declaration: KtElement): Boolean {
ApplicationManager.getApplication().assertIsWriteThread() ApplicationManager.getApplication().assertIsWriteThread()
val project = declaration.project val project = declaration.project
val ktModule = ProjectStructureProvider.getModule(project, declaration, contextualModule = null) val ktModule = ProjectStructureProvider.getModule(project, declaration, contextualModule = null)
val resolveSession = ktModule.getFirResolveSession(project) val resolveSession = ktModule.getFirResolveSession(project)
return when (val firDeclaration = declaration.resolveToFirSymbol(resolveSession).fir) {
val firDeclaration = when (declaration) {
is KtCodeFragment -> declaration.getOrBuildFirFile(resolveSession).codeFragment
is KtDeclaration -> declaration.resolveToFirSymbol(resolveSession).fir
else -> errorWithFirSpecificEntries("Unexpected declaration kind: ${declaration.javaClass.simpleName}", psi = declaration)
}
return when (firDeclaration) {
is FirSimpleFunction -> firDeclaration.inBodyInvalidation() is FirSimpleFunction -> firDeclaration.inBodyInvalidation()
is FirPropertyAccessor -> firDeclaration.inBodyInvalidation() is FirPropertyAccessor -> firDeclaration.inBodyInvalidation()
is FirProperty -> firDeclaration.inBodyInvalidation() is FirProperty -> firDeclaration.inBodyInvalidation()
is FirCodeFragment -> firDeclaration.inBodyInvalidation()
else -> errorWithFirSpecificEntries("Unknown declaration with body", fir = firDeclaration, psi = declaration) else -> errorWithFirSpecificEntries("Unknown declaration with body", fir = firDeclaration, psi = declaration)
} }
} }
@@ -145,6 +167,17 @@ private fun FirPropertyAccessor.inBodyInvalidation(): Boolean {
return true return true
} }
private fun FirCodeFragment.inBodyInvalidation(): Boolean {
if (block is FirLazyBlock) {
return true
}
decreasePhase(FirResolvePhase.BODY_RESOLVE.previous)
replaceBlock(buildLazyBlock())
return true
}
private fun FirProperty.invalidateInitializer(): PropertyExpressionState = replaceWithLazyExpressionIfNeeded(::initializer, ::replaceInitializer) private fun FirProperty.invalidateInitializer(): PropertyExpressionState = replaceWithLazyExpressionIfNeeded(::initializer, ::replaceInitializer)
private fun FirProperty.invalidateDelegate(): PropertyExpressionState = replaceWithLazyExpressionIfNeeded(::delegate, ::replaceDelegate) private fun FirProperty.invalidateDelegate(): PropertyExpressionState = replaceWithLazyExpressionIfNeeded(::delegate, ::replaceDelegate)
@@ -87,7 +87,7 @@ private inline fun <reified T : FirDeclaration> revive(
session = session, session = session,
scopeProvider = session.kotlinScopeProvider, scopeProvider = session.kotlinScopeProvider,
designation = designation, designation = designation,
rootNonLocalDeclaration = psiFactory(designation) as KtDeclaration, rootNonLocalDeclaration = psiFactory(designation) as KtElement,
) as T ) as T
} }
@@ -265,6 +265,14 @@ private fun needCalculatingLazyBodyForProperty(firProperty: FirProperty): Boolea
|| firProperty.delegate is FirLazyExpression || firProperty.delegate is FirLazyExpression
|| firProperty.getExplicitBackingField()?.initializer is FirLazyExpression || firProperty.getExplicitBackingField()?.initializer is FirLazyExpression
private fun calculateLazyBodyForCodeFragment(designation: FirDesignation) {
val codeFragment = designation.target as FirCodeFragment
require(codeFragment.block is FirLazyBlock)
val newCodeFragment = revive<FirCodeFragment>(designation)
codeFragment.replaceBlock(newCodeFragment.block)
}
private enum class FirLazyAnnotationTransformerScope { private enum class FirLazyAnnotationTransformerScope {
ALL_ANNOTATIONS, ALL_ANNOTATIONS,
COMPILER_ONLY; COMPILER_ONLY;
@@ -472,4 +480,13 @@ private abstract class FirLazyBodiesCalculatorTransformer : FirTransformer<Persi
return anonymousInitializer return anonymousInitializer
} }
override fun transformCodeFragment(codeFragment: FirCodeFragment, data: PersistentList<FirRegularClass>): FirCodeFragment {
if (codeFragment.block is FirLazyBlock) {
val designation = FirDesignation(data, codeFragment)
calculateLazyBodyForCodeFragment(designation)
}
return codeFragment
}
} }
@@ -36,7 +36,7 @@ internal class RawFirNonLocalDeclarationBuilder private constructor(
session: FirSession, session: FirSession,
baseScopeProvider: FirScopeProvider, baseScopeProvider: FirScopeProvider,
private val originalDeclaration: FirDeclaration, private val originalDeclaration: FirDeclaration,
private val declarationToBuild: KtDeclaration, private val declarationToBuild: KtElement,
private val functionsToRebind: Set<FirFunction>? = null, private val functionsToRebind: Set<FirFunction>? = null,
private val replacementApplier: RawFirReplacement.Applier? = null, private val replacementApplier: RawFirReplacement.Applier? = null,
) : PsiRawFirBuilder(session, baseScopeProvider, bodyBuildingMode = BodyBuildingMode.NORMAL) { ) : PsiRawFirBuilder(session, baseScopeProvider, bodyBuildingMode = BodyBuildingMode.NORMAL) {
@@ -58,24 +58,16 @@ internal class RawFirNonLocalDeclarationBuilder private constructor(
replacement: RawFirReplacement?, replacement: RawFirReplacement?,
): FirDeclaration { ): FirDeclaration {
val replacementApplier = replacement?.Applier() val replacementApplier = replacement?.Applier()
val builder = RawFirNonLocalDeclarationBuilder( val result = build(session, scopeProvider, designation, rootNonLocalDeclaration, replacementApplier = replacementApplier)
session = session, replacementApplier?.ensureApplied()
baseScopeProvider = scopeProvider, return result
originalDeclaration = designation.target as FirDeclaration,
declarationToBuild = rootNonLocalDeclaration,
replacementApplier = replacementApplier
)
builder.context.packageFqName = rootNonLocalDeclaration.containingKtFile.packageFqName
return builder.moveNext(designation.path.iterator(), containingClass = null).also {
replacementApplier?.ensureApplied()
}
} }
fun buildWithFunctionSymbolRebind( fun buildWithFunctionSymbolRebind(
session: FirSession, session: FirSession,
scopeProvider: FirScopeProvider, scopeProvider: FirScopeProvider,
designation: FirDesignation, designation: FirDesignation,
rootNonLocalDeclaration: KtDeclaration, rootNonLocalDeclaration: KtElement,
): FirDeclaration { ): FirDeclaration {
val functionsToRebind = when (val originalDeclaration = designation.target) { val functionsToRebind = when (val originalDeclaration = designation.target) {
is FirFunction -> setOf(originalDeclaration) is FirFunction -> setOf(originalDeclaration)
@@ -83,12 +75,26 @@ internal class RawFirNonLocalDeclarationBuilder private constructor(
else -> null else -> null
} }
return build(session, scopeProvider, designation, rootNonLocalDeclaration, functionsToRebind)
}
fun build(
session: FirSession,
scopeProvider: FirScopeProvider,
designation: FirDesignation,
rootNonLocalDeclaration: KtElement,
functionsToRebind: Set<FirFunction>? = null,
replacementApplier: RawFirReplacement.Applier? = null
): FirDeclaration {
check(rootNonLocalDeclaration is KtDeclaration || rootNonLocalDeclaration is KtCodeFragment)
val builder = RawFirNonLocalDeclarationBuilder( val builder = RawFirNonLocalDeclarationBuilder(
session = session, session = session,
baseScopeProvider = scopeProvider, baseScopeProvider = scopeProvider,
originalDeclaration = designation.target as FirDeclaration, originalDeclaration = designation.target as FirDeclaration,
declarationToBuild = rootNonLocalDeclaration, declarationToBuild = rootNonLocalDeclaration,
functionsToRebind = functionsToRebind, functionsToRebind = functionsToRebind,
replacementApplier = replacementApplier
) )
builder.context.packageFqName = rootNonLocalDeclaration.containingKtFile.packageFqName builder.context.packageFqName = rootNonLocalDeclaration.containingKtFile.packageFqName
return builder.moveNext(designation.path.iterator(), containingClass = null) return builder.moveNext(designation.path.iterator(), containingClass = null)
@@ -162,7 +162,7 @@ internal fun LLFirAbstractBodyTargetResolver.transformAnnotations(target: FirEle
target.transformSingle(transformer, ResolutionMode.ContextIndependent) target.transformSingle(transformer, ResolutionMode.ContextIndependent)
} }
target is FirFile -> {} target is FirCodeFragment || target is FirFile -> {}
else -> throwUnexpectedFirElementError(target) else -> throwUnexpectedFirElementError(target)
} }
@@ -5,11 +5,18 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignationWithFile import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirResolveTarget import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeFileResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.asResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.throwUnexpectedFirElementError import org.jetbrains.kotlin.analysis.low.level.api.fir.api.throwUnexpectedFirElementError
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LLFirLockProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LLFirLockProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyBodiesCalculator import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyBodiesCalculator
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.state.LLFirResolvableResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkDelegatedConstructorIsResolved import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkDelegatedConstructorIsResolved
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.* import org.jetbrains.kotlin.analysis.low.level.api.fir.util.*
import org.jetbrains.kotlin.fir.utils.exceptions.withFirEntry import org.jetbrains.kotlin.fir.utils.exceptions.withFirEntry
@@ -25,19 +32,28 @@ import org.jetbrains.kotlin.fir.expressions.builder.buildLazyDelegatedConstructo
import org.jetbrains.kotlin.fir.expressions.builder.buildMultiDelegatedConstructorCall import org.jetbrains.kotlin.fir.expressions.builder.buildMultiDelegatedConstructorCall
import org.jetbrains.kotlin.fir.expressions.impl.FirContractCallBlock import org.jetbrains.kotlin.fir.expressions.impl.FirContractCallBlock
import org.jetbrains.kotlin.fir.expressions.impl.FirLazyDelegatedConstructorCall import org.jetbrains.kotlin.fir.expressions.impl.FirLazyDelegatedConstructorCall
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.references.FirThisReference import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.references.builder.buildExplicitSuperReference import org.jetbrains.kotlin.fir.references.builder.buildExplicitSuperReference
import org.jetbrains.kotlin.fir.references.builder.buildExplicitThisReference import org.jetbrains.kotlin.fir.references.builder.buildExplicitThisReference
import org.jetbrains.kotlin.fir.resolve.FirCodeFragmentContext
import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.dfa.FirControlFlowGraphReferenceImpl import org.jetbrains.kotlin.fir.resolve.dfa.FirControlFlowGraphReferenceImpl
import org.jetbrains.kotlin.fir.resolve.codeFragmentContext
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirResolveContextCollector import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirResolveContextCollector
import org.jetbrains.kotlin.fir.resolve.transformers.contracts.FirContractsDslNames import org.jetbrains.kotlin.fir.resolve.transformers.contracts.FirContractsDslNames
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.isUsedInControlFlowGraphBuilderForClass import org.jetbrains.kotlin.fir.resolve.dfa.cfg.isUsedInControlFlowGraphBuilderForClass
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.utils.exceptions.buildErrorWithAttachment
import org.jetbrains.kotlin.utils.exceptions.requireWithAttachment import org.jetbrains.kotlin.utils.exceptions.requireWithAttachment
import org.jetbrains.kotlin.utils.findIsInstanceAnd import org.jetbrains.kotlin.utils.findIsInstanceAnd
@@ -100,6 +116,14 @@ private class LLFirBodyTargetResolver(
calculateControlFlowGraph(target) calculateControlFlowGraph(target)
} }
return true
}
is FirCodeFragment -> {
resolveCodeFragmentContext(target)
performCustomResolveUnderLock(target) {
resolve(target, BodyStateKeepers.CODE_FRAGMENT)
}
return true return true
} }
} }
@@ -136,9 +160,57 @@ private class LLFirBodyTargetResolver(
} }
} }
private fun resolveCodeFragmentContext(firCodeFragment: FirCodeFragment) {
val ktCodeFragment = firCodeFragment.psi as? KtCodeFragment
?: errorWithAttachment("Code fragment source not found") {
withFirEntry("firCodeFragment", firCodeFragment)
}
val module = firCodeFragment.llFirModuleData.ktModule
val resolveSession = module.getFirResolveSession(ktCodeFragment.project) as LLFirResolvableResolveSession
val contextPsiElement = ktCodeFragment.context
val contextKtFile = contextPsiElement?.containingFile as? KtFile
firCodeFragment.codeFragmentContext = if (contextKtFile != null) {
val contextFirFile = resolveSession.getOrBuildFirFile(contextKtFile)
val target = computeCodeFragmentContextResolveTarget(contextFirFile, contextPsiElement)
val contextParentKtElements = contextPsiElement.parentsWithSelf.toSet()
val contextProvider = ContextCollector.process(target, transformer.components) { candidate ->
when (candidate) {
contextPsiElement -> ContextCollector.FilterResponse.STOP
in contextParentKtElements -> ContextCollector.FilterResponse.CONTINUE
else -> ContextCollector.FilterResponse.SKIP
}
}
val elementContext = contextProvider[contextPsiElement, ContextCollector.ContextKind.BODY]
?: contextParentKtElements.firstNotNullOf { contextProvider[it, ContextCollector.ContextKind.SELF] }
LLFirCodeFragmentContext(elementContext.towerDataContext, elementContext.smartCasts)
} else {
val towerDataContext = FirTowerDataContext()
LLFirCodeFragmentContext(towerDataContext, emptyMap())
}
}
private fun computeCodeFragmentContextResolveTarget(contextFirFile: FirFile, contextPsiElement: PsiElement): LLFirResolveTarget {
val contextKtDeclaration = contextPsiElement.getNonLocalContainingOrThisDeclaration()
if (contextKtDeclaration != null) {
val designationPath = FirElementFinder.collectDesignationPath(contextFirFile, contextKtDeclaration)
if (designationPath != null) {
return FirDesignationWithFile(designationPath.path, designationPath.target, contextFirFile).asResolveTarget()
}
}
return LLFirWholeFileResolveTarget(contextFirFile)
}
override fun doLazyResolveUnderLock(target: FirElementWithResolveState) { override fun doLazyResolveUnderLock(target: FirElementWithResolveState) {
when (target) { when (target) {
is FirRegularClass -> error("Should have been resolved in ${::doResolveWithoutLock.name}") is FirRegularClass, is FirCodeFragment -> error("Should have been resolved in ${::doResolveWithoutLock.name}")
is FirConstructor -> resolve(target, BodyStateKeepers.CONSTRUCTOR) is FirConstructor -> resolve(target, BodyStateKeepers.CONSTRUCTOR)
is FirFunction -> resolve(target, BodyStateKeepers.FUNCTION) is FirFunction -> resolve(target, BodyStateKeepers.FUNCTION)
is FirProperty -> resolve(target, BodyStateKeepers.PROPERTY) is FirProperty -> resolve(target, BodyStateKeepers.PROPERTY)
@@ -209,6 +281,10 @@ internal object BodyStateKeepers {
) )
} }
val CODE_FRAGMENT: StateKeeper<FirCodeFragment, FirDesignationWithFile> = stateKeeper { _, _ ->
add(FirCodeFragment::block, FirCodeFragment::replaceBlock, ::blockGuard)
}
val ANONYMOUS_INITIALIZER: StateKeeper<FirAnonymousInitializer, FirDesignationWithFile> = stateKeeper { _, _ -> val ANONYMOUS_INITIALIZER: StateKeeper<FirAnonymousInitializer, FirDesignationWithFile> = stateKeeper { _, _ ->
add(FirAnonymousInitializer::body, FirAnonymousInitializer::replaceBody, ::blockGuard) add(FirAnonymousInitializer::body, FirAnonymousInitializer::replaceBody, ::blockGuard)
add(FirAnonymousInitializer::controlFlowGraphReference, FirAnonymousInitializer::replaceControlFlowGraphReference) add(FirAnonymousInitializer::controlFlowGraphReference, FirAnonymousInitializer::replaceControlFlowGraphReference)
@@ -409,4 +485,9 @@ private fun requireSameSize(old: List<FirStatement>, new: List<FirStatement>) {
new.forEachIndexed { index, statement -> withFirEntry("statement$index", statement) } new.forEachIndexed { index, statement -> withFirEntry("statement$index", statement) }
} }
} }
} }
private class LLFirCodeFragmentContext(
override val towerDataContext: FirTowerDataContext,
override val variables: Map<FirBasedSymbol<*>, Set<ConeKotlinType>>
) : FirCodeFragmentContext
@@ -103,9 +103,14 @@ private class LLFirCompilerRequiredAnnotationsTargetResolver(
override fun doResolveWithoutLock(target: FirElementWithResolveState): Boolean { override fun doResolveWithoutLock(target: FirElementWithResolveState): Boolean {
if (target is FirFile) return false if (target is FirFile) return false
when {
target is FirRegularClass || target is FirScript || target.isRegularDeclarationWithAnnotation -> {} when (target) {
else -> throwUnexpectedFirElementError(target) is FirRegularClass, is FirScript, is FirCodeFragment -> {}
else -> {
if (!target.isRegularDeclarationWithAnnotation) {
throwUnexpectedFirElementError(target)
}
}
} }
requireIsInstance<FirAnnotationContainer>(target) requireIsInstance<FirAnnotationContainer>(target)
@@ -65,6 +65,7 @@ private class LLFirContractsTargetResolver(
is FirAnonymousInitializer, is FirAnonymousInitializer,
is FirFile, is FirFile,
is FirScript, is FirScript,
is FirCodeFragment,
is FirFileAnnotationsContainer, is FirFileAnnotationsContainer,
is FirDanglingModifierList, is FirDanglingModifierList,
-> { -> {
@@ -78,6 +78,7 @@ internal class LLFirImplicitBodyTargetResolver(
is FirRegularClass, is FirRegularClass,
is FirTypeAlias, is FirTypeAlias,
is FirFile, is FirFile,
is FirCodeFragment,
is FirAnonymousInitializer, is FirAnonymousInitializer,
is FirDanglingModifierList, is FirDanglingModifierList,
is FirFileAnnotationsContainer, is FirFileAnnotationsContainer,
@@ -131,7 +131,7 @@ private class LLFirTypeTargetResolver(
is FirRegularClass -> { is FirRegularClass -> {
resolveClassTypes(target) resolveClassTypes(target)
} }
is FirFile, is FirAnonymousInitializer -> {} is FirFile, is FirAnonymousInitializer, is FirCodeFragment -> {}
else -> errorWithAttachment("Unknown declaration ${target::class}") { else -> errorWithAttachment("Unknown declaration ${target::class}") {
withFirEntry("declaration", target) withFirEntry("declaration", target)
} }
@@ -105,6 +105,15 @@ internal object FirElementFinder {
continue continue
} }
subDeclaration is FirCodeFragment -> {
val codeFragmentDeclarations = subDeclaration.block.statements.asSequence().filterIsInstance<FirDeclaration>()
if (find(codeFragmentDeclarations.asIterable(), classIdPathIndex)) {
return true
}
continue
}
subDeclaration is FirRegularClass && currentClassSegment == subDeclaration.symbol.name -> { subDeclaration is FirRegularClass && currentClassSegment == subDeclaration.symbol.name -> {
path += subDeclaration path += subDeclaration
if (find(subDeclaration.declarations, classIdPathIndex + 1)) { if (find(subDeclaration.declarations, classIdPathIndex + 1)) {
@@ -26,7 +26,7 @@ fun FirElementWithResolveState.getContainingFile(): FirFile? {
is FirCallableDeclaration -> provider.getFirCallableContainerFile(symbol) is FirCallableDeclaration -> provider.getFirCallableContainerFile(symbol)
is FirClassLikeDeclaration -> provider.getFirClassifierContainerFileIfAny(symbol) is FirClassLikeDeclaration -> provider.getFirClassifierContainerFileIfAny(symbol)
is FirAnonymousInitializer -> containingClass().getContainingFile() is FirAnonymousInitializer -> containingClass().getContainingFile()
is FirDanglingModifierList -> { is FirDanglingModifierList, is FirCodeFragment -> {
val ktFile = psi?.containingFile as? KtFile val ktFile = psi?.containingFile as? KtFile
?: error("File for dangling modifier list cannot be null") ?: error("File for dangling modifier list cannot be null")
val moduleComponents = llFirResolvableSession?.moduleComponents val moduleComponents = llFirResolvableSession?.moduleComponents
@@ -196,6 +196,12 @@ private fun KtClassLikeDeclaration.findFir(provider: FirProvider): FirClassLikeD
} }
} }
val FirFile.codeFragment: FirCodeFragment
get() {
return declarations.singleOrNull() as? FirCodeFragment
?: errorWithFirSpecificEntries("Code fragment not found in a FirFile", fir = this)
}
val FirDeclaration.isGeneratedDeclaration val FirDeclaration.isGeneratedDeclaration
get() = realPsi == null get() = realPsi == null
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.compile
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.containingDeclarationProvider.AbstractContainingDeclarationProviderByDelegatedMemberScopeTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.containingDeclarationProvider.AbstractContainingDeclarationProviderByDelegatedMemberScopeTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.containingDeclarationProvider.AbstractContainingDeclarationProviderByMemberScopeTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.containingDeclarationProvider.AbstractContainingDeclarationProviderByMemberScopeTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.containingDeclarationProvider.AbstractContainingDeclarationProviderByPsiTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.containingDeclarationProvider.AbstractContainingDeclarationProviderByPsiTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.diagnosticProvider.AbstractCodeFragmentCollectDiagnosticsTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.diagnosticProvider.AbstractCollectDiagnosticsTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.diagnosticProvider.AbstractCollectDiagnosticsTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.expressionInfoProvider.AbstractIsUsedAsExpressionTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.expressionInfoProvider.AbstractIsUsedAsExpressionTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.expressionInfoProvider.AbstractReturnTargetSymbolTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.expressionInfoProvider.AbstractReturnTargetSymbolTest
@@ -265,6 +266,10 @@ private fun AnalysisApiTestGroup.generateAnalysisApiComponentsTests() {
test(AbstractCollectDiagnosticsTest::class) { test(AbstractCollectDiagnosticsTest::class) {
model("diagnostics") model("diagnostics")
} }
test(AbstractCodeFragmentCollectDiagnosticsTest::class, filter = frontendIs(FrontendKind.Fir)) {
model("codeFragmentDiagnostics", pattern = TestGeneratorUtil.KT_WITHOUT_DOTS_IN_NAME)
}
} }
// for K1, symbols do not have a proper equality implementation, so the tests are failing // for K1, symbols do not have a proper equality implementation, so the tests are failing