[LL API] Support code fragment analysis
This commit is contained in:
+66
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+66
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
class Foo {
|
||||||
|
val a = x
|
||||||
|
val b = y
|
||||||
|
}
|
||||||
|
|
||||||
|
Foo().b
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
fun test() {
|
||||||
|
val x = 0
|
||||||
|
<caret>val y = 0
|
||||||
|
}
|
||||||
analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics/localClass.txt
Vendored
+4
@@ -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)
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo(): Pair<Int, Int> {
|
||||||
|
return x to y
|
||||||
|
}
|
||||||
|
|
||||||
|
foo()
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// WITH_STDLIB
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val x = 0
|
||||||
|
<caret>val y = 0
|
||||||
|
}
|
||||||
+4
@@ -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)
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
foo(x)
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
fun test() {
|
||||||
|
val x = 0
|
||||||
|
<caret>val y = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(a: String) {}
|
||||||
+4
@@ -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)
|
||||||
+2
-2
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+36
@@ -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.
|
||||||
|
|||||||
+1
-2
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -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
|
||||||
|
|||||||
+1
@@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+8
@@ -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)
|
||||||
|
|||||||
+46
-2
@@ -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,
|
||||||
|
|||||||
+36
-3
@@ -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)
|
||||||
|
|||||||
+18
-1
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-13
@@ -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)
|
||||||
|
|||||||
+1
-1
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+83
-2
@@ -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
|
||||||
+8
-3
@@ -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)
|
||||||
|
|||||||
+1
@@ -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,
|
||||||
-> {
|
-> {
|
||||||
|
|||||||
+1
@@ -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,
|
||||||
|
|||||||
+1
-1
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -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)) {
|
||||||
|
|||||||
+1
-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
|
||||||
|
|||||||
+6
@@ -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
|
||||||
|
|
||||||
|
|||||||
+5
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user