KT-61689 [LL API] Handle scripts in ContextCollector

Add `FirDesignationWithScript`, because regular designation
does not account for scripts at all - that makes it
cumbersome to add the script's top level and implicit
receiver scopes when collecting contexts for the
non-top-level declarations (class members, for example)

^KT-61689 Fixed
This commit is contained in:
Roman Golyshev
2023-09-04 16:00:17 +02:00
committed by Space Team
parent 83049dc25c
commit c9e4b71010
10 changed files with 420 additions and 2 deletions
@@ -45,6 +45,12 @@ class FirDesignationWithFile(
}
}
class FirDesignationWithScript(
path: List<FirRegularClass>,
target: FirElementWithResolveState,
val firScript: FirScript,
): FirDesignation(path, target)
open class FirDesignation(
val path: List<FirRegularClass>,
val target: FirElementWithResolveState,
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.util
import com.intellij.openapi.progress.ProgressManager
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignation
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignationWithScript
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ContextCollector.ContextKind
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ContextCollector.Context
@@ -35,6 +36,7 @@ import org.jetbrains.kotlin.fir.types.typeContext
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.utils.yieldIfNotNull
internal object ContextCollector {
enum class ContextKind {
@@ -106,7 +108,13 @@ internal object ContextCollector {
if (contextKtDeclaration != null) {
val designationPath = FirElementFinder.collectDesignationPath(file, contextKtDeclaration)
if (designationPath != null) {
return FirDesignation(designationPath.path, designationPath.target)
val script = file.declarations.singleOrNull() as? FirScript
return if (script == null || script === designationPath.target) {
FirDesignation(designationPath.path, designationPath.target)
} else {
FirDesignationWithScript(designationPath.path, designationPath.target, script)
}
}
}
@@ -138,6 +146,7 @@ internal object ContextCollector {
private class DesignationInterceptor(private val designation: FirDesignation) : () -> FirElement? {
private val targetIterator = iterator {
yieldIfNotNull((designation as? FirDesignationWithScript)?.firScript)
yieldAll(designation.path)
yield(designation.target)
}
@@ -278,6 +287,14 @@ private class ContextCollectorVisitor(
return node !is ClassExitNode
}
override fun visitScript(script: FirScript) {
context.withScopesForScript(script, holder) {
withInterceptor {
super.visitScript(script)
}
}
}
override fun visitFile(file: FirFile) {
context.withFile(file, holder) {
withInterceptor {
@@ -0,0 +1,24 @@
/*
* 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.
*/
fun before_Fun() {}
val before_Val = 10
class Before_OtherClass
class MyClass {
val foo = 10
fun usage() {
<expr>foo</expr>
}
}
fun after_Fun() {}
val after_Val = 10
class After_OtherClass
@@ -0,0 +1,105 @@
Tower Data Context:
Element 0
Scope: FirDefaultStarImportingScope
Element 1
Scope: FirDefaultSimpleImportingScope
Element 2
Scope: FirExplicitStarImportingScope
Element 3
Scope: FirDefaultSimpleImportingScope
Element 4
Scope: FirDefaultSimpleImportingScope
Element 5
Scope: FirPackageMemberScope
Element 6
Scope: FirExplicitSimpleImportingScope
Element 7
Scope: FirScriptDeclarationsScope
Classifiers:
FirRegularClassSymbol public? final? class After_OtherClass : R|kotlin/Any|
FirRegularClassSymbol public? final? class Before_OtherClass : R|kotlin/Any|
FirRegularClassSymbol public final class MyClass : R|kotlin/Any|
Functions
FirNamedFunctionSymbol public? final? fun after_Fun(): R|kotlin/Unit|
FirNamedFunctionSymbol public? final? fun before_Fun(): R|kotlin/Unit|
Properties:
FirPropertySymbol public? final? val after_Val: <implicit>
public? get(): <implicit>
FirPropertySymbol public? final? val before_Val: <implicit>
public? get(): <implicit>
Element 8
Scope: FirLocalScope
Classifiers:
FirRegularClassSymbol public final class MyClass : R|kotlin/Any|
Properties:
FirPropertySymbol lval args: R|kotlin/Array<kotlin/String>|
Element 9
Context receivers:
FirScriptSymbol context(<script>@R|kotlin/script/templates/standard/ScriptTemplateWithArgs|)
SCRIPT:
lval args: R|kotlin/Array<kotlin/String>|
public? final? fun before_Fun(): R|kotlin/Unit|
public? final? val before_Val: <implicit>
public? get(): <implicit>
public? final? class Before_OtherClass : R|kotlin/Any|
public final class MyClass : R|kotlin/Any|
public? final? fun after_Fun(): R|kotlin/Unit|
public? final? val after_Val: <implicit>
public? get(): <implicit>
public? final? class After_OtherClass : R|kotlin/Any|
Type: kotlin/script/templates/standard/ScriptTemplateWithArgs
Label: <script>
Element 10
Implicit receiver:
FirRegularClassSymbol public final class MyClass : R|kotlin/Any|
Type: MyClass
Element 11
Scope: FirLocalScope
Element 12
Scope: FirLocalScope
FILE: [ResolvedTo(IMPORTS)] insideClassMember.kts
context(<script>@R|kotlin/script/templates/standard/ScriptTemplateWithArgs|)
SCRIPT: [ResolvedTo(TYPES)]
[ResolvedTo(RAW_FIR)] lval args: R|kotlin/Array<kotlin/String>|
public? final? [ResolvedTo(RAW_FIR)] fun before_Fun(): R|kotlin/Unit| { LAZY_BLOCK }
public? final? [ResolvedTo(RAW_FIR)] val before_Val: <implicit> = LAZY_EXPRESSION
public? [ResolvedTo(RAW_FIR)] get(): <implicit>
public? final? [ResolvedTo(RAW_FIR)] class Before_OtherClass : R|kotlin/Any| {
public? [ResolvedTo(RAW_FIR)] constructor(): R|Before_OtherClass| {
LAZY_super<R|kotlin/Any|>
}
}
public final [ResolvedTo(STATUS)] class MyClass : R|kotlin/Any| {
public [ResolvedTo(STATUS)] constructor(): R|MyClass| {
LAZY_super<R|kotlin/Any|>
}
public final [ResolvedTo(IMPLICIT_TYPES_BODY_RESOLVE)] val foo: R|kotlin/Int| = Int(10)
public [ResolvedTo(IMPLICIT_TYPES_BODY_RESOLVE)] get(): R|kotlin/Int|
public final [ResolvedTo(BODY_RESOLVE)] fun usage(): R|kotlin/Unit| {
this@R|/MyClass|.R|/MyClass.foo|
}
}
public? final? [ResolvedTo(RAW_FIR)] fun after_Fun(): R|kotlin/Unit| { LAZY_BLOCK }
public? final? [ResolvedTo(RAW_FIR)] val after_Val: <implicit> = LAZY_EXPRESSION
public? [ResolvedTo(RAW_FIR)] get(): <implicit>
public? final? [ResolvedTo(RAW_FIR)] class After_OtherClass : R|kotlin/Any| {
public? [ResolvedTo(RAW_FIR)] constructor(): R|After_OtherClass| {
LAZY_super<R|kotlin/Any|>
}
}
@@ -0,0 +1,20 @@
/*
* 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.
*/
fun before_Fun() {}
val before_Val = 10
class Before_OtherClass
fun usage(foo: Int) {
<expr>foo</expr>
}
fun after_Fun() {}
val after_Val = 10
class After_OtherClass
@@ -0,0 +1,93 @@
Tower Data Context:
Element 0
Scope: FirDefaultStarImportingScope
Element 1
Scope: FirDefaultSimpleImportingScope
Element 2
Scope: FirExplicitStarImportingScope
Element 3
Scope: FirDefaultSimpleImportingScope
Element 4
Scope: FirDefaultSimpleImportingScope
Element 5
Scope: FirPackageMemberScope
Element 6
Scope: FirExplicitSimpleImportingScope
Element 7
Scope: FirScriptDeclarationsScope
Classifiers:
FirRegularClassSymbol public? final? class After_OtherClass : R|kotlin/Any|
FirRegularClassSymbol public? final? class Before_OtherClass : R|kotlin/Any|
Functions
FirNamedFunctionSymbol public? final? fun after_Fun(): R|kotlin/Unit|
FirNamedFunctionSymbol public? final? fun before_Fun(): R|kotlin/Unit|
FirNamedFunctionSymbol public final fun usage(foo: R|kotlin/Int|): R|kotlin/Unit|
Properties:
FirPropertySymbol public? final? val after_Val: <implicit>
public? get(): <implicit>
FirPropertySymbol public? final? val before_Val: <implicit>
public? get(): <implicit>
Element 8
Scope: FirLocalScope
Functions
FirNamedFunctionSymbol public final fun usage(foo: R|kotlin/Int|): R|kotlin/Unit|
Properties:
FirPropertySymbol lval args: R|kotlin/Array<kotlin/String>|
Element 9
Context receivers:
FirScriptSymbol context(<script>@R|kotlin/script/templates/standard/ScriptTemplateWithArgs|)
SCRIPT:
lval args: R|kotlin/Array<kotlin/String>|
public? final? fun before_Fun(): R|kotlin/Unit|
public? final? val before_Val: <implicit>
public? get(): <implicit>
public? final? class Before_OtherClass : R|kotlin/Any|
public final fun usage(foo: R|kotlin/Int|): R|kotlin/Unit|
public? final? fun after_Fun(): R|kotlin/Unit|
public? final? val after_Val: <implicit>
public? get(): <implicit>
public? final? class After_OtherClass : R|kotlin/Any|
Type: kotlin/script/templates/standard/ScriptTemplateWithArgs
Label: <script>
Element 10
Scope: FirLocalScope
Properties:
FirValueParameterSymbol foo: R|kotlin/Int|
Element 11
Scope: FirLocalScope
FILE: [ResolvedTo(IMPORTS)] insideTopLevelFunction.kts
context(<script>@R|kotlin/script/templates/standard/ScriptTemplateWithArgs|)
SCRIPT: [ResolvedTo(TYPES)]
[ResolvedTo(RAW_FIR)] lval args: R|kotlin/Array<kotlin/String>|
public? final? [ResolvedTo(RAW_FIR)] fun before_Fun(): R|kotlin/Unit| { LAZY_BLOCK }
public? final? [ResolvedTo(RAW_FIR)] val before_Val: <implicit> = LAZY_EXPRESSION
public? [ResolvedTo(RAW_FIR)] get(): <implicit>
public? final? [ResolvedTo(RAW_FIR)] class Before_OtherClass : R|kotlin/Any| {
public? [ResolvedTo(RAW_FIR)] constructor(): R|Before_OtherClass| {
LAZY_super<R|kotlin/Any|>
}
}
public final [ResolvedTo(BODY_RESOLVE)] fun usage([ResolvedTo(BODY_RESOLVE)] foo: R|kotlin/Int|): R|kotlin/Unit| {
R|<local>/foo|
}
public? final? [ResolvedTo(RAW_FIR)] fun after_Fun(): R|kotlin/Unit| { LAZY_BLOCK }
public? final? [ResolvedTo(RAW_FIR)] val after_Val: <implicit> = LAZY_EXPRESSION
public? [ResolvedTo(RAW_FIR)] get(): <implicit>
public? final? [ResolvedTo(RAW_FIR)] class After_OtherClass : R|kotlin/Any| {
public? [ResolvedTo(RAW_FIR)] constructor(): R|After_OtherClass| {
LAZY_super<R|kotlin/Any|>
}
}
@@ -0,0 +1,18 @@
/*
* 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.
*/
fun before_Fun() {}
val before_Val = 10
class Before_OtherClass
<expr>args</expr>
fun after_Fun() {}
val after_Val = 10
class After_OtherClass
@@ -0,0 +1,87 @@
Tower Data Context:
Element 0
Scope: FirDefaultStarImportingScope
Element 1
Scope: FirDefaultSimpleImportingScope
Element 2
Scope: FirExplicitStarImportingScope
Element 3
Scope: FirDefaultSimpleImportingScope
Element 4
Scope: FirDefaultSimpleImportingScope
Element 5
Scope: FirPackageMemberScope
Element 6
Scope: FirExplicitSimpleImportingScope
Element 7
Scope: FirScriptDeclarationsScope
Classifiers:
FirRegularClassSymbol public final class After_OtherClass : R|kotlin/Any|
FirRegularClassSymbol public final class Before_OtherClass : R|kotlin/Any|
Functions
FirNamedFunctionSymbol public final fun after_Fun(): R|kotlin/Unit|
FirNamedFunctionSymbol public final fun before_Fun(): R|kotlin/Unit|
Properties:
FirPropertySymbol public final val after_Val: R|kotlin/Int|
public get(): R|kotlin/Int|
FirPropertySymbol public final val before_Val: R|kotlin/Int|
public get(): R|kotlin/Int|
Element 8
Scope: FirLocalScope
Classifiers:
FirRegularClassSymbol public final class Before_OtherClass : R|kotlin/Any|
Functions
FirNamedFunctionSymbol public final fun before_Fun(): R|kotlin/Unit|
Properties:
FirPropertySymbol lval args: R|kotlin/Array<kotlin/String>|
Element 9
Context receivers:
FirScriptSymbol context(<script>@R|kotlin/script/templates/standard/ScriptTemplateWithArgs|)
SCRIPT:
lval args: R|kotlin/Array<kotlin/String>|
public final fun before_Fun(): R|kotlin/Unit|
public final val before_Val: R|kotlin/Int|
public get(): R|kotlin/Int|
public final class Before_OtherClass : R|kotlin/Any|
args#
public final fun after_Fun(): R|kotlin/Unit|
public final val after_Val: R|kotlin/Int|
public get(): R|kotlin/Int|
public final class After_OtherClass : R|kotlin/Any|
Type: kotlin/script/templates/standard/ScriptTemplateWithArgs
Label: <script>
FILE: [ResolvedTo(IMPORTS)] topLevel.kts
context(<script>@R|kotlin/script/templates/standard/ScriptTemplateWithArgs|)
SCRIPT: [ResolvedTo(TYPES)]
[ResolvedTo(RAW_FIR)] lval args: R|kotlin/Array<kotlin/String>|
public final [ResolvedTo(BODY_RESOLVE)] fun before_Fun(): R|kotlin/Unit| {
}
public final [ResolvedTo(BODY_RESOLVE)] val before_Val: R|kotlin/Int| = Int(10)
public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/Int|
public final [ResolvedTo(STATUS)] class Before_OtherClass : R|kotlin/Any| {
public [ResolvedTo(BODY_RESOLVE)] constructor(): R|Before_OtherClass| {
super<R|kotlin/Any|>()
}
}
args#
public final [ResolvedTo(BODY_RESOLVE)] fun after_Fun(): R|kotlin/Unit| {
}
public final [ResolvedTo(BODY_RESOLVE)] val after_Val: R|kotlin/Int| = Int(10)
public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/Int|
public final [ResolvedTo(STATUS)] class After_OtherClass : R|kotlin/Any| {
public [ResolvedTo(STATUS)] constructor(): R|After_OtherClass| {
LAZY_super<R|kotlin/Any|>
}
}
@@ -23,4 +23,42 @@ public class ContextCollectorScriptTestGenerated extends AbstractContextCollecto
public void testAllFilesPresentInContextCollector() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testdata/contextCollector"), Pattern.compile("^(.+)\\.(kts)$"), null, true);
}
@Nested
@TestMetadata("analysis/low-level-api-fir/testdata/contextCollector/scripts")
@TestDataPath("$PROJECT_ROOT")
public class Scripts {
@Test
public void testAllFilesPresentInScripts() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testdata/contextCollector/scripts"), Pattern.compile("^(.+)\\.(kts)$"), null, true);
}
@Test
@TestMetadata("insideClassMember.kts")
public void testInsideClassMember() throws Exception {
runTest("analysis/low-level-api-fir/testdata/contextCollector/scripts/insideClassMember.kts");
}
@Test
@TestMetadata("insideTopLevelFunction.kts")
public void testInsideTopLevelFunction() throws Exception {
runTest("analysis/low-level-api-fir/testdata/contextCollector/scripts/insideTopLevelFunction.kts");
}
@Test
@TestMetadata("topLevel.kts")
public void testTopLevel() throws Exception {
runTest("analysis/low-level-api-fir/testdata/contextCollector/scripts/topLevel.kts");
}
}
@Nested
@TestMetadata("analysis/low-level-api-fir/testdata/contextCollector/smartCasts")
@TestDataPath("$PROJECT_ROOT")
public class SmartCasts {
@Test
public void testAllFilesPresentInSmartCasts() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testdata/contextCollector/smartCasts"), Pattern.compile("^(.+)\\.(kts)$"), null, true);
}
}
}
@@ -96,6 +96,16 @@ public class ContextCollectorSourceTestGenerated extends AbstractContextCollecto
runTest("analysis/low-level-api-fir/testdata/contextCollector/simple.kt");
}
@Nested
@TestMetadata("analysis/low-level-api-fir/testdata/contextCollector/scripts")
@TestDataPath("$PROJECT_ROOT")
public class Scripts {
@Test
public void testAllFilesPresentInScripts() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testdata/contextCollector/scripts"), Pattern.compile("^(.+)\\.(kt)$"), null, true);
}
}
@Nested
@TestMetadata("analysis/low-level-api-fir/testdata/contextCollector/smartCasts")
@TestDataPath("$PROJECT_ROOT")
@@ -114,7 +124,7 @@ public class ContextCollectorSourceTestGenerated extends AbstractContextCollecto
@Test
public void testAllFilesPresentInSmartCasts() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testdata/contextCollector/smartCasts"), Pattern.compile("^(.+)\\.kt$"), null, true);
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testdata/contextCollector/smartCasts"), Pattern.compile("^(.+)\\.(kt)$"), null, true);
}
@Test