[LL API] Add basic tests for 'ContextCollector'
This commit is contained in:
+24
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.test.framework.services
|
|||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiWhiteSpace
|
import com.intellij.psi.PsiWhiteSpace
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
|
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
|
||||||
import org.jetbrains.kotlin.analysis.utils.printer.parentOfType
|
import org.jetbrains.kotlin.analysis.utils.printer.parentOfType
|
||||||
import org.jetbrains.kotlin.util.PrivateForInline
|
import org.jetbrains.kotlin.util.PrivateForInline
|
||||||
@@ -15,6 +16,8 @@ import org.jetbrains.kotlin.psi.KtElement
|
|||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
|
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
|
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
|
||||||
import org.jetbrains.kotlin.test.model.TestFile
|
import org.jetbrains.kotlin.test.model.TestFile
|
||||||
import org.jetbrains.kotlin.test.model.TestModule
|
import org.jetbrains.kotlin.test.model.TestModule
|
||||||
@@ -155,6 +158,27 @@ class ExpressionMarkerProvider : TestService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the bottommost element of an [elementType] or its subtype located precisely in the [range].
|
||||||
|
*/
|
||||||
|
private fun <T : PsiElement> getBottommostElementOfTypeInRange(file: KtFile, range: TextRange, elementType: Class<T>): T {
|
||||||
|
var candidate = PsiTreeUtil.findElementOfClassAtOffset(file, range.startOffset, elementType, true)
|
||||||
|
while (candidate != null && candidate.endOffset < range.endOffset) {
|
||||||
|
candidate = PsiTreeUtil.getParentOfType(candidate, elementType)?.takeIf { it.startOffset == range.startOffset }
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidate?.takeIf { it.endOffset == range.endOffset }
|
||||||
|
?: error("Cannot find '${elementType.name}' in range $range")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the bottommost element of [E] or its subtype wrapped in an '<expr>' selection tag.
|
||||||
|
*/
|
||||||
|
fun <E : KtElement> getBottommostSelectedElementOfType(file: KtFile, elementType: Class<E>): E {
|
||||||
|
val range = selected[file.name] ?: error("No selected expression found in file")
|
||||||
|
return getBottommostElementOfTypeInRange(file, range, elementType)
|
||||||
|
}
|
||||||
|
|
||||||
private fun List<PsiElement>.trimWhitespaces(): List<PsiElement> =
|
private fun List<PsiElement>.trimWhitespaces(): List<PsiElement> =
|
||||||
dropWhile { it is PsiWhiteSpace }
|
dropWhile { it is PsiWhiteSpace }
|
||||||
.dropLastWhile { it is PsiWhiteSpace }
|
.dropLastWhile { it is PsiWhiteSpace }
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
interface Foo {
|
||||||
|
fun foo(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Bar {
|
||||||
|
fun bar(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
context(Foo, Bar)
|
||||||
|
fun test() {
|
||||||
|
<expr>foo() + bar()</expr>
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
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: FirLocalScope
|
||||||
|
Element 8
|
||||||
|
Context receivers:
|
||||||
|
FirNamedFunctionSymbol context(R|Foo|, R|Bar|)
|
||||||
|
public final fun test(): R|kotlin/Unit|
|
||||||
|
Type: Foo
|
||||||
|
Label: Foo
|
||||||
|
FirNamedFunctionSymbol context(R|Foo|, R|Bar|)
|
||||||
|
public final fun test(): R|kotlin/Unit|
|
||||||
|
Type: Bar
|
||||||
|
Label: Bar
|
||||||
|
Element 9
|
||||||
|
Scope: FirLocalScope
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
interface Foo {
|
||||||
|
fun foo(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
context(Foo)
|
||||||
|
class Test {
|
||||||
|
fun test() {
|
||||||
|
<expr>foo()</expr>
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
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
|
||||||
|
Implicit receiver:
|
||||||
|
FirRegularClassSymbol context(R|Foo|)
|
||||||
|
public final class Test : R|kotlin/Any|
|
||||||
|
Type: Test
|
||||||
|
Element 8
|
||||||
|
Context receivers:
|
||||||
|
FirRegularClassSymbol context(R|Foo|)
|
||||||
|
public final class Test : R|kotlin/Any|
|
||||||
|
Type: Foo
|
||||||
|
Label: Foo
|
||||||
|
Element 9
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Element 10
|
||||||
|
Scope: FirLocalScope
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun String.test() {
|
||||||
|
<expr>consume(this)</expr>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun consume(obj: Any) {}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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: FirLocalScope
|
||||||
|
Element 8
|
||||||
|
Implicit receiver:
|
||||||
|
FirNamedFunctionSymbol public final fun R|kotlin/String|.test(): R|kotlin/Unit|
|
||||||
|
Type: kotlin/String
|
||||||
|
Element 9
|
||||||
|
Scope: FirLocalScope
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
fun test() {
|
||||||
|
block("foo") foo@{
|
||||||
|
block(42) num@{
|
||||||
|
<expr>consume(this@foo.length + this@num)</expr>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> block(reciever: T, block: T.() -> Unit) {
|
||||||
|
receiver.block()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun consume(num: Int) {}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
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: FirLocalScope
|
||||||
|
Element 8
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Element 9
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Element 10
|
||||||
|
Implicit receiver:
|
||||||
|
FirAnonymousFunctionSymbol foo@fun R|kotlin/String|.<anonymous>(): R|kotlin/Unit| <inline=NoInline>
|
||||||
|
Type: kotlin/String
|
||||||
|
Element 11
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Element 12
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Element 13
|
||||||
|
Implicit receiver:
|
||||||
|
FirAnonymousFunctionSymbol num@fun R|kotlin/Int|.<anonymous>(): R|kotlin/Unit| <inline=NoInline>
|
||||||
|
Type: kotlin/Int
|
||||||
|
Element 14
|
||||||
|
Scope: FirLocalScope
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class Foo<F> {
|
||||||
|
inner class Bar<B> {
|
||||||
|
fun test(f: F, b: B) {
|
||||||
|
<expr>consume(f)</expr>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun consume(obj: Any) {}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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: FirMemberTypeParameterScope
|
||||||
|
Classifiers:
|
||||||
|
FirTypeParameterSymbol F
|
||||||
|
Element 8
|
||||||
|
Scope: FirNestedClassifierScopeImpl
|
||||||
|
Classifiers:
|
||||||
|
FirRegularClassSymbol public final inner class Bar<B, F> : R|kotlin/Any|
|
||||||
|
Element 9
|
||||||
|
Implicit receiver:
|
||||||
|
FirRegularClassSymbol public final class Foo<F> : R|kotlin/Any|
|
||||||
|
Type: test/Foo<F>
|
||||||
|
Element 10
|
||||||
|
Scope: FirMemberTypeParameterScope
|
||||||
|
Classifiers:
|
||||||
|
FirTypeParameterSymbol B
|
||||||
|
Element 11
|
||||||
|
Implicit receiver:
|
||||||
|
FirRegularClassSymbol public final inner class Bar<B, F> : R|kotlin/Any|
|
||||||
|
Type: test/Foo.Bar<B, F>
|
||||||
|
Element 12
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Properties:
|
||||||
|
FirValueParameterSymbol b: R|B|
|
||||||
|
FirValueParameterSymbol f: R|F|
|
||||||
|
Element 13
|
||||||
|
Scope: FirLocalScope
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fun test() {
|
||||||
|
block("foo") { s ->
|
||||||
|
block(42) { n ->
|
||||||
|
<expr>s.length + n</expr>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> block(obj: T, block: (T) -> Unit) {
|
||||||
|
block(obj)
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
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: FirLocalScope
|
||||||
|
Element 8
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Element 9
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Properties:
|
||||||
|
FirValueParameterSymbol s: R|kotlin/String|
|
||||||
|
Element 10
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Element 11
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Properties:
|
||||||
|
FirValueParameterSymbol n: R|kotlin/Int|
|
||||||
|
Element 12
|
||||||
|
Scope: FirLocalScope
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class Foo<F> {
|
||||||
|
class Bar<B> {
|
||||||
|
fun test(b: B) {
|
||||||
|
<expr>consume(b)</expr>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun consume(obj: Any) {}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
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: FirNestedClassifierScopeImpl
|
||||||
|
Classifiers:
|
||||||
|
FirRegularClassSymbol public final class Bar<B> : R|kotlin/Any|
|
||||||
|
Element 8
|
||||||
|
Scope: FirMemberTypeParameterScope
|
||||||
|
Classifiers:
|
||||||
|
FirTypeParameterSymbol B
|
||||||
|
Element 9
|
||||||
|
Implicit receiver:
|
||||||
|
FirRegularClassSymbol public final class Bar<B> : R|kotlin/Any|
|
||||||
|
Type: test/Foo.Bar<B>
|
||||||
|
Element 10
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Properties:
|
||||||
|
FirValueParameterSymbol b: R|B|
|
||||||
|
Element 11
|
||||||
|
Scope: FirLocalScope
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
val a: Int
|
||||||
|
|
||||||
|
fun foo(p: String) {
|
||||||
|
val x = 1.0f
|
||||||
|
<expr>print(x)</expr>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
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
|
||||||
|
Implicit receiver:
|
||||||
|
FirRegularClassSymbol public final class Foo : R|kotlin/Any|
|
||||||
|
Type: foo/Foo
|
||||||
|
Element 8
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Properties:
|
||||||
|
FirValueParameterSymbol p: R|kotlin/String|
|
||||||
|
Element 9
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Properties:
|
||||||
|
FirPropertySymbol lval x: R|kotlin/Float|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
interface Foo {}
|
||||||
|
|
||||||
|
fun test(obj: Any) {
|
||||||
|
if (obj is Foo) {
|
||||||
|
consume(<expr>obj</expr>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun consume(obj: Any) {}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
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: FirLocalScope
|
||||||
|
Properties:
|
||||||
|
FirValueParameterSymbol obj: R|kotlin/Any|
|
||||||
|
Element 8
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Element 9
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Smart Casts:
|
||||||
|
FirValueParameterSymbol obj: R|kotlin/Any|
|
||||||
|
Types:
|
||||||
|
Foo
|
||||||
|
kotlin/Any
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
interface Foo {}
|
||||||
|
interface Bar {}
|
||||||
|
|
||||||
|
fun test(obj: Any) {
|
||||||
|
if (obj is Foo) {
|
||||||
|
if (obj is Bar) {
|
||||||
|
obj.consume(<expr>obj</expr>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Bar.consume(obj: Foo) {}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
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: FirLocalScope
|
||||||
|
Properties:
|
||||||
|
FirValueParameterSymbol obj: R|kotlin/Any|
|
||||||
|
Element 8
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Element 9
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Element 10
|
||||||
|
Scope: FirLocalScope
|
||||||
|
Smart Casts:
|
||||||
|
FirValueParameterSymbol obj: R|kotlin/Any|
|
||||||
|
Types:
|
||||||
|
Foo
|
||||||
|
kotlin/Any
|
||||||
|
Bar
|
||||||
+242
@@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
* 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.low.level.api.fir
|
||||||
|
|
||||||
|
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.test.configurators.AnalysisApiFirSourceTestConfigurator
|
||||||
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ContextCollector
|
||||||
|
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
||||||
|
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedSingleModuleTest
|
||||||
|
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirResolvedImport
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirTowerDataContext
|
||||||
|
import org.jetbrains.kotlin.fir.renderer.FirRenderer
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.SessionHolderImpl
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.*
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.impl.*
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||||
|
import org.jetbrains.kotlin.fir.types.renderReadableWithFqNames
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.name.SpecialNames
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.test.model.TestModule
|
||||||
|
import org.jetbrains.kotlin.test.services.TestServices
|
||||||
|
import org.jetbrains.kotlin.test.services.assertions
|
||||||
|
import java.lang.IllegalArgumentException
|
||||||
|
|
||||||
|
abstract class AbstractContextCollectorTest : AbstractAnalysisApiBasedSingleModuleTest() {
|
||||||
|
override val configurator = AnalysisApiFirSourceTestConfigurator(analyseInDependentSession = false)
|
||||||
|
|
||||||
|
override fun doTestByFileStructure(ktFiles: List<KtFile>, module: TestModule, testServices: TestServices) {
|
||||||
|
val mainKtFile = ktFiles.singleOrNull() ?: ktFiles.single { it.name == "main.kt" }
|
||||||
|
|
||||||
|
val project = mainKtFile.project
|
||||||
|
val sourceModule = ProjectStructureProvider.getModule(project, mainKtFile, contextualModule = null)
|
||||||
|
|
||||||
|
val resolveSession = sourceModule.getFirResolveSession(project)
|
||||||
|
val session = resolveSession.useSiteFirSession
|
||||||
|
val sessionHolder = SessionHolderImpl(session, session.getScopeSession())
|
||||||
|
|
||||||
|
val firFile = mainKtFile.getOrBuildFirFile(resolveSession)
|
||||||
|
|
||||||
|
val targetElement = testServices.expressionMarkerProvider
|
||||||
|
.getBottommostSelectedElementOfType(mainKtFile, KtElement::class.java)
|
||||||
|
|
||||||
|
val elementContext = ContextCollector.process(firFile, sessionHolder, targetElement) { it === targetElement }
|
||||||
|
?: error("Context not found for element $targetElement")
|
||||||
|
|
||||||
|
val actualText = ElementContextRenderer.render(elementContext)
|
||||||
|
testServices.assertions.assertEqualsToTestDataFileSibling(actualText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object ElementContextRenderer {
|
||||||
|
fun render(context: ContextCollector.Context): String {
|
||||||
|
return buildString {
|
||||||
|
renderTowerDataContext(context.towerDataContext)
|
||||||
|
renderSmartCasts(context.smartCasts)
|
||||||
|
}.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.renderTowerDataContext(towerDataContext: FirTowerDataContext) {
|
||||||
|
appendBlock("Tower Data Context:") {
|
||||||
|
for ((index, towerDataElement) in towerDataContext.towerDataElements.withIndex()) {
|
||||||
|
appendBlock("Element $index") {
|
||||||
|
for (scope in towerDataElement.scope?.flatten().orEmpty()) {
|
||||||
|
appendBlock("Scope: " + scope.javaClass.simpleName) {
|
||||||
|
renderScope(scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
towerDataElement.implicitReceiver?.let { implicitReceiver ->
|
||||||
|
appendBlock("Implicit receiver:") {
|
||||||
|
appendSymbol(implicitReceiver.boundSymbol).appendLine()
|
||||||
|
|
||||||
|
appendBlock {
|
||||||
|
append("Type: ").appendType(implicitReceiver.type).appendLine()
|
||||||
|
if (implicitReceiver.isContextReceiver) {
|
||||||
|
append("Context receiver index: ").appendLine(implicitReceiver.contextReceiverNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
towerDataElement.contextReceiverGroup?.takeIf { it.isNotEmpty() }?.let { contextReceiverValues ->
|
||||||
|
appendBlock("Context receivers:") {
|
||||||
|
for (contextReceiverValue in contextReceiverValues) {
|
||||||
|
appendSymbol(contextReceiverValue.boundSymbol).appendLine()
|
||||||
|
|
||||||
|
appendBlock {
|
||||||
|
append("Type: ").appendType(contextReceiverValue.type).appendLine()
|
||||||
|
contextReceiverValue.labelName?.let { labelName ->
|
||||||
|
append("Label: ").appendLine(labelName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
towerDataElement.staticScopeOwnerSymbol?.let { staticScopeOwnerSymbol ->
|
||||||
|
append("Static scope owner symbol: ").appendSymbol(staticScopeOwnerSymbol).appendLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.renderScope(scope: FirScope) {
|
||||||
|
when (scope) {
|
||||||
|
is FirDefaultSimpleImportingScope, is FirDefaultStarImportingScope -> {
|
||||||
|
Unit
|
||||||
|
// Skip to avoid fixing default imports in an unrelated test
|
||||||
|
}
|
||||||
|
is FirPackageMemberScope -> {
|
||||||
|
// Skip as the scope can be huge
|
||||||
|
}
|
||||||
|
is FirAbstractSimpleImportingScope -> {
|
||||||
|
for (import in scope.simpleImports.flatMap { it.value }) {
|
||||||
|
renderImport(import)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is FirAbstractStarImportingScope -> {
|
||||||
|
for (import in scope.starImports) {
|
||||||
|
renderImport(import)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is FirContainingNamesAwareScope -> {
|
||||||
|
val classifierNames = scope.getClassifierNames().sorted()
|
||||||
|
val callableNames = scope.getCallableNames().sorted()
|
||||||
|
|
||||||
|
fun <T : FirBasedSymbol<*>> appendDeclarations(title: String, names: List<Name>, collector: (Name, (T) -> Unit) -> Unit) {
|
||||||
|
collect(names, collector).takeIf { it.isNotEmpty() }?.let { classifiers ->
|
||||||
|
appendBlock(title) {
|
||||||
|
classifiers.forEach { appendSymbol(it).appendLine() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendDeclarations("Classifiers:", classifierNames, scope::processClassifiersByName)
|
||||||
|
appendDeclarations("Functions", callableNames, scope::processFunctionsByName)
|
||||||
|
appendDeclarations("Properties:", callableNames, scope::processPropertiesByName)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
throw IllegalArgumentException("Unexpected scope type: " + scope.javaClass.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> collect(names: List<Name>, collector: (Name, (T) -> Unit) -> Unit): List<T> {
|
||||||
|
return buildList {
|
||||||
|
for (name in names) {
|
||||||
|
collector(name) { add(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.renderImport(import: FirResolvedImport) {
|
||||||
|
val name = import.importedName ?: SpecialNames.NO_NAME_PROVIDED
|
||||||
|
|
||||||
|
appendBlock("Import name:" + name.asString()) {
|
||||||
|
import.importedFqName?.let { importedFqName ->
|
||||||
|
append("Qualified name: ").append(importedFqName.asString()).appendLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
append("Is all under: ").append(import.isAllUnder).appendLine()
|
||||||
|
|
||||||
|
import.aliasName?.let { aliasName ->
|
||||||
|
append("Alias: ").append(aliasName.asString()).appendLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.renderSmartCasts(smartCasts: Map<FirBasedSymbol<*>, Set<ConeKotlinType>>) {
|
||||||
|
if (smartCasts.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
appendBlock("Smart Casts:") {
|
||||||
|
for ((symbol, types) in smartCasts) {
|
||||||
|
appendSymbol(symbol).appendLine()
|
||||||
|
|
||||||
|
appendBlock("Types:") {
|
||||||
|
for (type in types) {
|
||||||
|
appendType(type).appendLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.appendBlock(title: String? = null, block: StringBuilder.() -> Unit): StringBuilder {
|
||||||
|
if (title != null) {
|
||||||
|
appendLine(title)
|
||||||
|
}
|
||||||
|
|
||||||
|
val nestedText = StringBuilder().apply(block).toString().trim()
|
||||||
|
|
||||||
|
if (nestedText.isNotBlank()) {
|
||||||
|
for (line in nestedText.lineSequence()) {
|
||||||
|
appendIndent().appendLine(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.appendSymbol(symbol: FirBasedSymbol<*>): StringBuilder {
|
||||||
|
val renderer = FirRenderer(
|
||||||
|
annotationRenderer = null,
|
||||||
|
bodyRenderer = null,
|
||||||
|
classMemberRenderer = null,
|
||||||
|
contractRenderer = null
|
||||||
|
)
|
||||||
|
|
||||||
|
val type = symbol.javaClass.simpleName
|
||||||
|
val text = renderer.renderElementAsString(symbol.fir, trim = true)
|
||||||
|
return append(type).append(' ').append(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.appendType(type: ConeKotlinType): StringBuilder {
|
||||||
|
return append(type.renderReadableWithFqNames())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.appendIndent(): StringBuilder {
|
||||||
|
append(" ")
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirScope.flatten(): List<FirScope> {
|
||||||
|
return when (this) {
|
||||||
|
is FirCompositeScope -> scopes.flatMap { it.flatten() }
|
||||||
|
is FirNameAwareCompositeScope -> scopes.flatMap { it.flatten() }
|
||||||
|
else -> listOf(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* 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.low.level.api.fir;
|
||||||
|
|
||||||
|
import com.intellij.testFramework.TestDataPath;
|
||||||
|
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||||
|
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/low-level-api-fir/testdata/contextCollector")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
public class ContextCollectorTestGenerated extends AbstractContextCollectorTest {
|
||||||
|
@Test
|
||||||
|
public void testAllFilesPresentInContextCollector() throws Exception {
|
||||||
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testdata/contextCollector"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("contextReceivers.kt")
|
||||||
|
public void testContextReceivers() throws Exception {
|
||||||
|
runTest("analysis/low-level-api-fir/testdata/contextCollector/contextReceivers.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("contextReceiversClass.kt")
|
||||||
|
public void testContextReceiversClass() throws Exception {
|
||||||
|
runTest("analysis/low-level-api-fir/testdata/contextCollector/contextReceiversClass.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("extensionFunction.kt")
|
||||||
|
public void testExtensionFunction() throws Exception {
|
||||||
|
runTest("analysis/low-level-api-fir/testdata/contextCollector/extensionFunction.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("extensionLambdas.kt")
|
||||||
|
public void testExtensionLambdas() throws Exception {
|
||||||
|
runTest("analysis/low-level-api-fir/testdata/contextCollector/extensionLambdas.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("innerClasses.kt")
|
||||||
|
public void testInnerClasses() throws Exception {
|
||||||
|
runTest("analysis/low-level-api-fir/testdata/contextCollector/innerClasses.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("lambdaArguments.kt")
|
||||||
|
public void testLambdaArguments() throws Exception {
|
||||||
|
runTest("analysis/low-level-api-fir/testdata/contextCollector/lambdaArguments.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("nestedClasses.kt")
|
||||||
|
public void testNestedClasses() throws Exception {
|
||||||
|
runTest("analysis/low-level-api-fir/testdata/contextCollector/nestedClasses.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("simple.kt")
|
||||||
|
public void testSimple() throws Exception {
|
||||||
|
runTest("analysis/low-level-api-fir/testdata/contextCollector/simple.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("smartCastArgument.kt")
|
||||||
|
public void testSmartCastArgument() throws Exception {
|
||||||
|
runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCastArgument.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("smartCastReceiverArgument.kt")
|
||||||
|
public void testSmartCastReceiverArgument() throws Exception {
|
||||||
|
runTest("analysis/low-level-api-fir/testdata/contextCollector/smartCastReceiverArgument.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
@@ -179,6 +179,10 @@ internal fun TestGroupSuite.generateFirLowLevelApiTests() {
|
|||||||
testClass<AbstractCompilationPeerAnalysisTest> {
|
testClass<AbstractCompilationPeerAnalysisTest> {
|
||||||
model("compilationPeers")
|
model("compilationPeers")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testClass<AbstractContextCollectorTest> {
|
||||||
|
model("contextCollector")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testGroup("analysis/low-level-api-fir/tests", "analysis/analysis-api/testData") {
|
testGroup("analysis/low-level-api-fir/tests", "analysis/analysis-api/testData") {
|
||||||
|
|||||||
Reference in New Issue
Block a user