FIR IDE: introduce AbstractSymbolByReferencePointerRestoreTest

This commit is contained in:
Ilya Kirillov
2021-03-19 15:58:15 +01:00
parent 846dc1b9b9
commit 0bb37db4bf
46 changed files with 271 additions and 159 deletions
@@ -1040,7 +1040,11 @@ fun main(args: Array<String>) {
} }
testClass<AbstractSymbolFromLibraryPointerRestoreTest> { testClass<AbstractSymbolFromLibraryPointerRestoreTest> {
model("resoreSymbolFromLibrary", extension = "txt") model("resoreSymbolFromLibrary")
}
testClass<AbstractSymbolByReferencePointerRestoreTest> {
model("symbolByReference")
} }
testClass<AbstractMemoryLeakInSymbolsTest> { testClass<AbstractMemoryLeakInSymbolsTest> {
@@ -2,8 +2,7 @@ fun test(s: String) {
var i: Int = 0 var i: Int = 0
<expr>i</expr> = s.length <expr>i</expr> = s.length
} }
// RESULT
// RESULT
// expression: i // expression: i
// type: kotlin.Int // type: kotlin.Int
@@ -1,6 +1,5 @@
val x = <expr>1 + 2</expr> val x = <expr>1 + 2</expr>
// RESULT
// RESULT
// expression: 1 + 2 // expression: 1 + 2
// type: kotlin.Int // type: kotlin.Int
@@ -3,8 +3,7 @@ fun x(): Int {
<expr>break</expr> <expr>break</expr>
} }
} }
// RESULT
// RESULT
// expression: break // expression: break
// type: kotlin.Nothing // type: kotlin.Nothing
@@ -1,8 +1,7 @@
fun x(): Int { fun x(): Int {
<expr>for(i in 1..2) {}</expr> <expr>for(i in 1..2) {}</expr>
} }
// RESULT
// RESULT
// expression: for(i in 1..2) {} // expression: for(i in 1..2) {}
// type: kotlin.Unit // type: kotlin.Unit
@@ -1,7 +1,6 @@
val a = 12 val a = 12
val x = <expr>12.toByte()</expr> val x = <expr>12.toByte()</expr>
// RESULT
// RESULT
// expression: 12.toByte() // expression: 12.toByte()
// type: kotlin.Byte // type: kotlin.Byte
@@ -1,6 +1,5 @@
val x = <expr>(1 + 2)</expr> + 3 val x = <expr>(1 + 2)</expr> + 3
// RESULT
// RESULT
// expression: (1 + 2) // expression: (1 + 2)
// type: kotlin.Int // type: kotlin.Int
@@ -1,7 +1,6 @@
val a = 10 val a = 10
val x = "abc${<expr>a</expr>}defg" val x = "abc${<expr>a</expr>}defg"
// RESULT
// RESULT
// expression: a // expression: a
// type: kotlin.Int // type: kotlin.Int
@@ -1,7 +1,6 @@
val a = 10 val a = 10
val x = "abc${<expr>a</expr> + 20}defg" val x = "abc${<expr>a</expr> + 20}defg"
// RESULT
// RESULT
// expression: a // expression: a
// type: kotlin.Int // type: kotlin.Int
@@ -1,6 +1,5 @@
val x = <expr>1</expr> val x = <expr>1</expr>
// RESULT
// RESULT
// expression: 1 // expression: 1
// type: kotlin.Int // type: kotlin.Int
@@ -1,7 +1,6 @@
val a = 10 val a = 10
<expr>val x = 1</expr> <expr>val x = 1</expr>
// RESULT
// RESULT
// expression: val x = 1 // expression: val x = 1
// type: kotlin.Unit // type: kotlin.Unit
@@ -1,8 +1,7 @@
fun x(): Int { fun x(): Int {
<expr>return 1</expr> <expr>return 1</expr>
} }
// RESULT
// RESULT
// expression: return 1 // expression: return 1
// type: kotlin.Nothing // type: kotlin.Nothing
@@ -1,6 +1,5 @@
val x = <expr>"abc"</expr> val x = <expr>"abc"</expr>
// RESULT
// RESULT
// expression: "abc" // expression: "abc"
// type: kotlin.String // type: kotlin.String
@@ -1,8 +1,7 @@
fun x(): Int { fun x(): Int {
<expr>while(true) {}</expr> <expr>while(true) {}</expr>
} }
// RESULT
// RESULT
// expression: while(true) {} // expression: while(true) {}
// type: kotlin.Unit // type: kotlin.Unit
@@ -9,11 +9,10 @@ abstract class B {
abstract fun foo(x: String): Int abstract fun foo(x: String): Int
} }
// RESULT
// RESULT
// ALL: // ALL:
// B.foo(x: Int): Int // B.foo(x: Int): Int
// DIRECT: // DIRECT:
// B.foo(x: Int): Int // B.foo(x: Int): Int
@@ -22,8 +22,8 @@ interface D {
fun foo(x: String) fun foo(x: String)
} }
// RESULT
// RESULT
// ALL: // ALL:
// C.foo(x: String): Unit // C.foo(x: String): Unit
// D.foo(x: String): Unit // D.foo(x: String): Unit
@@ -31,4 +31,3 @@ interface D {
// DIRECT: // DIRECT:
// C.foo(x: String): Unit // C.foo(x: String): Unit
// D.foo(x: String): Unit // D.foo(x: String): Unit
@@ -22,8 +22,8 @@ interface D {
fun foo(x: String) fun foo(x: String)
} }
// RESULT
// RESULT
// ALL: // ALL:
// C.foo(x: String): Unit // C.foo(x: String): Unit
// D.foo(x: String): Unit // D.foo(x: String): Unit
@@ -31,4 +31,3 @@ interface D {
// DIRECT: // DIRECT:
// C.foo(x: String): Unit // C.foo(x: String): Unit
// D.foo(x: String): Unit // D.foo(x: String): Unit
@@ -16,12 +16,11 @@ abstract class C {
abstract val x: Int abstract val x: Int
} }
// RESULT
// RESULT
// ALL: // ALL:
// B.x: Int // B.x: Int
// C.x: Int // C.x: Int
// DIRECT: // DIRECT:
// B.x: Int // B.x: Int
@@ -25,8 +25,8 @@ interface D {
fun foo(x: String) fun foo(x: String)
} }
// RESULT
// RESULT
// ALL: // ALL:
// B.foo(x: String): Unit // B.foo(x: String): Unit
// C.foo(x: String): Unit // C.foo(x: String): Unit
@@ -36,4 +36,3 @@ interface D {
// B.foo(x: String): Unit // B.foo(x: String): Unit
// C.foo(x: String): Unit // C.foo(x: String): Unit
// D.foo(x: String): Unit // D.foo(x: String): Unit
@@ -18,8 +18,8 @@ open class D {
open fun foo(x: Int) {} open fun foo(x: Int) {}
} }
// RESULT
// RESULT
// ALL: // ALL:
// B.foo(x: Int): Unit // B.foo(x: Int): Unit
// C.foo(x: Int): Unit // C.foo(x: Int): Unit
@@ -27,4 +27,3 @@ open class D {
// DIRECT: // DIRECT:
// B.foo(x: Int): Unit // B.foo(x: Int): Unit
@@ -1,8 +1,9 @@
// RUNTIME // RUNTIME
class: kotlin/Lazy // class: kotlin/Lazy
// SYMBOLS: // RESULT
/*
KtFirNamedClassOrObjectSymbol: KtFirNamedClassOrObjectSymbol:
annotationClassIds: [] annotationClassIds: []
annotations: [] annotations: []
@@ -21,3 +22,4 @@ KtFirNamedClassOrObjectSymbol:
symbolKind: TOP_LEVEL symbolKind: TOP_LEVEL
typeParameters: [KtFirTypeParameterSymbol(T)] typeParameters: [KtFirTypeParameterSymbol(T)]
visibility: PUBLIC visibility: PUBLIC
*/
@@ -1,8 +1,9 @@
// RUNTIME // RUNTIME
class: java/lang/String // class: java/lang/String
// SYMBOLS: // RESULT
/*
KtFirNamedClassOrObjectSymbol: KtFirNamedClassOrObjectSymbol:
annotationClassIds: [] annotationClassIds: []
annotations: [] annotations: []
@@ -21,3 +22,4 @@ KtFirNamedClassOrObjectSymbol:
symbolKind: TOP_LEVEL symbolKind: TOP_LEVEL
typeParameters: [] typeParameters: []
visibility: PUBLIC visibility: PUBLIC
*/
@@ -1,11 +1,13 @@
// RUNTIME // RUNTIME
callable: kotlin/LazyThreadSafetyMode.SYNCHRONIZED // callable: kotlin/LazyThreadSafetyMode.SYNCHRONIZED
// SYMBOLS: // RESULT
/*
KtFirEnumEntrySymbol: KtFirEnumEntrySymbol:
annotatedType: [] kotlin/LazyThreadSafetyMode annotatedType: [] kotlin/LazyThreadSafetyMode
containingEnumClassIdIfNonLocal: kotlin/LazyThreadSafetyMode containingEnumClassIdIfNonLocal: kotlin/LazyThreadSafetyMode
name: SYNCHRONIZED name: SYNCHRONIZED
origin: LIBRARY origin: LIBRARY
symbolKind: MEMBER symbolKind: MEMBER
*/
@@ -1,6 +1,7 @@
callable: kotlin/collections/List.get // callable: kotlin/collections/List.get
// SYMBOLS: // RESULT
/*
KtFirFunctionSymbol: KtFirFunctionSymbol:
annotatedType: [] E annotatedType: [] E
annotationClassIds: [] annotationClassIds: []
@@ -21,3 +22,4 @@ KtFirFunctionSymbol:
typeParameters: [] typeParameters: []
valueParameters: [KtFirFunctionValueParameterSymbol(index)] valueParameters: [KtFirFunctionValueParameterSymbol(index)]
visibility: PUBLIC visibility: PUBLIC
*/
@@ -1,6 +1,7 @@
callable: kotlin/collections/List.listIterator // callable: kotlin/collections/List.listIterator
// SYMBOLS: // RESULT
/*
KtFirFunctionSymbol: KtFirFunctionSymbol:
annotatedType: [] kotlin/collections/ListIterator<E> annotatedType: [] kotlin/collections/ListIterator<E>
annotationClassIds: [] annotationClassIds: []
@@ -42,3 +43,4 @@ KtFirFunctionSymbol:
typeParameters: [] typeParameters: []
valueParameters: [KtFirFunctionValueParameterSymbol(index)] valueParameters: [KtFirFunctionValueParameterSymbol(index)]
visibility: PUBLIC visibility: PUBLIC
*/
@@ -1,6 +1,7 @@
class: kotlin/collections/MutableMap.MutableEntry // class: kotlin/collections/MutableMap.MutableEntry
// SYMBOLS: // RESULT
/*
KtFirNamedClassOrObjectSymbol: KtFirNamedClassOrObjectSymbol:
annotationClassIds: [] annotationClassIds: []
annotations: [] annotations: []
@@ -19,3 +20,4 @@ KtFirNamedClassOrObjectSymbol:
symbolKind: MEMBER symbolKind: MEMBER
typeParameters: [KtFirTypeParameterSymbol(K), KtFirTypeParameterSymbol(V)] typeParameters: [KtFirTypeParameterSymbol(K), KtFirTypeParameterSymbol(V)]
visibility: PUBLIC visibility: PUBLIC
*/
@@ -3,3 +3,13 @@ var x: Int = 0
set(value) { set(value) {
field = value field = value
} }
// RESULT
/*
KtFirBackingFieldSymbol:
annotatedType: [] kotlin/Int
name: field
origin: PROPERTY_BACKING_FIELD
owningProperty: KtFirKotlinPropertySymbol(x)
symbolKind: LOCAL
*/
@@ -3,8 +3,8 @@
fun x() { fun x() {
val a = <caret>ArrayList(listOf(1)) val a = <caret>ArrayList(listOf(1))
} }
// RESULT
// RESULT
/* /*
KtFirConstructorSymbol: KtFirConstructorSymbol:
annotatedType: [] java/util/ArrayList<E> annotatedType: [] java/util/ArrayList<E>
@@ -19,4 +19,3 @@ KtFirConstructorSymbol:
valueParameters: [KtFirConstructorValueParameterSymbol(c)] valueParameters: [KtFirConstructorValueParameterSymbol(c)]
visibility: PUBLIC visibility: PUBLIC
*/ */
+3 -1
View File
@@ -1,7 +1,8 @@
class A { class A {
} }
// SYMBOLS: // RESULT
/*
KtFirNamedClassOrObjectSymbol: KtFirNamedClassOrObjectSymbol:
annotationClassIds: [] annotationClassIds: []
annotations: [] annotations: []
@@ -20,3 +21,4 @@ KtFirNamedClassOrObjectSymbol:
symbolKind: TOP_LEVEL symbolKind: TOP_LEVEL
typeParameters: [] typeParameters: []
visibility: PUBLIC visibility: PUBLIC
*/
@@ -1,7 +1,8 @@
class A() { class A() {
} }
// SYMBOLS: // RESULT
/*
KtFirConstructorSymbol: KtFirConstructorSymbol:
annotatedType: [] A annotatedType: [] A
annotationClassIds: [] annotationClassIds: []
@@ -33,3 +34,4 @@ KtFirNamedClassOrObjectSymbol:
symbolKind: TOP_LEVEL symbolKind: TOP_LEVEL
typeParameters: [] typeParameters: []
visibility: PUBLIC visibility: PUBLIC
*/
@@ -3,7 +3,8 @@ class A() {
constructor(y: Int, z: String) : this(y) constructor(y: Int, z: String) : this(y)
} }
// SYMBOLS: // RESULT
/*
KtFirConstructorSymbol: KtFirConstructorSymbol:
annotatedType: [] A annotatedType: [] A
annotationClassIds: [] annotationClassIds: []
@@ -91,3 +92,4 @@ KtFirNamedClassOrObjectSymbol:
symbolKind: TOP_LEVEL symbolKind: TOP_LEVEL
typeParameters: [] typeParameters: []
visibility: PUBLIC visibility: PUBLIC
*/
+3 -1
View File
@@ -2,7 +2,8 @@ enum class X {
Y, Z; Y, Z;
} }
// SYMBOLS: // RESULT
/*
KtFirEnumEntrySymbol: KtFirEnumEntrySymbol:
annotatedType: [] X annotatedType: [] X
containingEnumClassIdIfNonLocal: X containingEnumClassIdIfNonLocal: X
@@ -35,3 +36,4 @@ KtFirNamedClassOrObjectSymbol:
symbolKind: TOP_LEVEL symbolKind: TOP_LEVEL
typeParameters: [] typeParameters: []
visibility: PUBLIC visibility: PUBLIC
*/
@@ -3,7 +3,8 @@ class A {
fun y() fun y()
} }
// SYMBOLS: // RESULT
/*
KtFirFunctionSymbol: KtFirFunctionSymbol:
annotatedType: [] kotlin/Int annotatedType: [] kotlin/Int
annotationClassIds: [] annotationClassIds: []
@@ -64,3 +65,4 @@ KtFirNamedClassOrObjectSymbol:
symbolKind: TOP_LEVEL symbolKind: TOP_LEVEL
typeParameters: [] typeParameters: []
visibility: PUBLIC visibility: PUBLIC
*/
@@ -3,7 +3,8 @@ class A {
val Int.y get() = this val Int.y get() = this
} }
// SYMBOLS: // RESULT
/*
KtFirKotlinPropertySymbol: KtFirKotlinPropertySymbol:
annotatedType: [] kotlin/Int annotatedType: [] kotlin/Int
annotationClassIds: [] annotationClassIds: []
@@ -84,3 +85,4 @@ KtFirNamedClassOrObjectSymbol:
symbolKind: TOP_LEVEL symbolKind: TOP_LEVEL
typeParameters: [] typeParameters: []
visibility: PUBLIC visibility: PUBLIC
*/
@@ -1,7 +1,8 @@
fun x(): Int = 10 fun x(): Int = 10
fun y() {} fun y() {}
// SYMBOLS: // RESULT
/*
KtFirFunctionSymbol: KtFirFunctionSymbol:
annotatedType: [] kotlin/Int annotatedType: [] kotlin/Int
annotationClassIds: [] annotationClassIds: []
@@ -43,3 +44,4 @@ KtFirFunctionSymbol:
typeParameters: [] typeParameters: []
valueParameters: [] valueParameters: []
visibility: PUBLIC visibility: PUBLIC
*/
@@ -1,7 +1,8 @@
val x: Int = 10 val x: Int = 10
val Int.y get() = this val Int.y get() = this
// SYMBOLS: // RESULT
/*
KtFirKotlinPropertySymbol: KtFirKotlinPropertySymbol:
annotatedType: [] kotlin/Int annotatedType: [] kotlin/Int
annotationClassIds: [] annotationClassIds: []
@@ -63,3 +64,4 @@ KtFirKotlinPropertySymbol:
setter: null setter: null
symbolKind: TOP_LEVEL symbolKind: TOP_LEVEL
visibility: PUBLIC visibility: PUBLIC
*/
@@ -14,12 +14,13 @@ import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescrip
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File import java.io.File
import java.nio.file.Paths
abstract class AbstractSymbolByFqNameTest : KotlinLightCodeInsightFixtureTestCase() { abstract class AbstractSymbolByFqNameTest : KotlinLightCodeInsightFixtureTestCase() {
protected fun doTest(path: String) { protected fun doTest(path: String) {
val fakeKtFile = myFixture.configureByText("file.kt", "fun a() {}") as KtFile val fakeKtFile = myFixture.configureByText("file.kt", "fun a() {}") as KtFile
val symbolData = SymbolByFqName.getSymbolDataFromFile(path) val symbolData = SymbolByFqName.getSymbolDataFromFile(Paths.get(path))
val renderedSymbols = executeOnPooledThreadInReadAction { val renderedSymbols = executeOnPooledThreadInReadAction {
analyze(fakeKtFile) { analyze(fakeKtFile) {
@@ -13,12 +13,17 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import java.io.File import java.io.File
import java.nio.file.Path
object SymbolByFqName { object SymbolByFqName {
fun getSymbolDataFromFile(filePath: String): SymbolData { fun getSymbolDataFromFile(filePath: Path): SymbolData {
val testFileText = FileUtil.loadFile(File(filePath)) val testFileText = FileUtil.loadFile(filePath.toFile())
val identifier = testFileText.lineSequence().first { line -> SymbolData.identifiers.any { line.startsWith(it) } } val identifier = testFileText.lineSequence().first { line ->
return SymbolData.create(identifier) SymbolData.identifiers.any { identifier ->
line.startsWith(identifier) || line.startsWith("// $identifier")
}
}
return SymbolData.create(identifier.removePrefix("// "))
} }
fun textWithRenderedSymbolData(filePath: String, rendered: String): String = buildString { fun textWithRenderedSymbolData(filePath: String, rendered: String): String = buildString {
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2020 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.idea.frontend.api.symbols
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.test.framework.TestFileStructure
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
abstract class AbstractSymbolByReferencePointerRestoreTest : AbstractSymbolPointerRestoreTest() {
override fun KtAnalysisSession.collectSymbols(fileStructure: TestFileStructure): List<KtSymbol> {
val referenceExpression = getElementOfTypeAtCaret<KtNameReferenceExpression>()
return listOfNotNull(referenceExpression.mainReference.resolveToSymbol())
}
}
@@ -27,7 +27,8 @@ abstract class AbstractSymbolByReferenceTest : AbstractKtIdeaTest() {
val actual = TestStructureRenderer.render( val actual = TestStructureRenderer.render(
fileStructure, fileStructure,
TestStructureExpectedDataBlock(renderedSymbol) TestStructureExpectedDataBlock(renderedSymbol),
renderingMode = TestStructureRenderer.RenderingMode.ALL_BLOCKS_IN_MULTI_LINE_COMMENT,
) )
KotlinTestUtils.assertEqualsToFile(fileStructure.filePath.toFile(), actual) KotlinTestUtils.assertEqualsToFile(fileStructure.filePath.toFile(), actual)
} }
@@ -7,11 +7,12 @@ package org.jetbrains.kotlin.idea.frontend.api.symbols
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.SymbolByFqName import org.jetbrains.kotlin.idea.frontend.api.SymbolByFqName
import org.jetbrains.kotlin.idea.test.framework.TestFileStructure
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
abstract class AbstractSymbolFromLibraryPointerRestoreTest : AbstractSymbolPointerRestoreTest() { abstract class AbstractSymbolFromLibraryPointerRestoreTest : AbstractSymbolPointerRestoreTest() {
override fun KtAnalysisSession.collectSymbols(filePath: String, ktFile: KtFile): List<KtSymbol> { override fun KtAnalysisSession.collectSymbols(fileStructure: TestFileStructure): List<KtSymbol> {
val symbolData = SymbolByFqName.getSymbolDataFromFile(filePath) val symbolData = SymbolByFqName.getSymbolDataFromFile(fileStructure.filePath)
return with(symbolData) { toSymbols() } return with(symbolData) { toSymbols() }
} }
} }
@@ -6,13 +6,14 @@
package org.jetbrains.kotlin.idea.frontend.api.symbols package org.jetbrains.kotlin.idea.frontend.api.symbols
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.test.framework.TestFileStructure
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
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
abstract class AbstractSymbolFromSourcePointerRestoreTest : AbstractSymbolPointerRestoreTest() { abstract class AbstractSymbolFromSourcePointerRestoreTest : AbstractSymbolPointerRestoreTest() {
override fun KtAnalysisSession.collectSymbols(filePath: String, ktFile: KtFile): List<KtSymbol> = override fun KtAnalysisSession.collectSymbols(fileStructure: TestFileStructure): List<KtSymbol> =
ktFile.collectDescendantsOfType<KtDeclaration>().map { declaration -> fileStructure.mainKtFile.collectDescendantsOfType<KtDeclaration>().map { declaration ->
declaration.getSymbol() declaration.getSymbol()
} }
} }
@@ -7,74 +7,81 @@ package org.jetbrains.kotlin.idea.frontend.api.symbols
import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.editor.Document
import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiDocumentManager
import org.jetbrains.kotlin.idea.executeOnPooledThreadInReadAction import org.jetbrains.kotlin.idea.analyseOnPooledThreadInReadAction
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.SymbolByFqName
import org.jetbrains.kotlin.idea.frontend.api.analyze
import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.framework.AbstractKtIdeaTest
import org.jetbrains.kotlin.idea.test.framework.TestFileStructure
import org.jetbrains.kotlin.idea.test.framework.TestStructureExpectedDataBlock
import org.jetbrains.kotlin.idea.test.framework.TestStructureRenderer
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.util.suffixIfNot
import org.junit.Assert
import java.io.File
abstract class AbstractSymbolPointerRestoreTest : KotlinLightCodeInsightFixtureTestCase() { abstract class AbstractSymbolPointerRestoreTest : AbstractKtIdeaTest() {
abstract fun KtAnalysisSession.collectSymbols(filePath: String, ktFile: KtFile): List<KtSymbol> abstract fun KtAnalysisSession.collectSymbols(fileStructure: TestFileStructure): List<KtSymbol>
protected fun doTest(path: String) { override fun doTestByFileStructure(fileStructure: TestFileStructure) {
val file = File(path) val pointersWithRendered = analyseOnPooledThreadInReadAction(fileStructure.mainKtFile) {
val ktFile = myFixture.configureByText(file.name.suffixIfNot(".kt"), FileUtil.loadFile(file)) as KtFile collectSymbols(fileStructure).map { symbol ->
PointerWithRenderedSymbol(
val pointersWithRendered = executeOnPooledThreadInReadAction { symbol.createPointer(),
analyze(ktFile) { DebugSymbolRenderer.render(symbol)
collectSymbols(path, ktFile).map { symbol -> )
PointerWithRenderedSymbol(
symbol.createPointer(),
DebugSymbolRenderer.render(symbol)
)
}
} }
} }
val actual = SymbolByFqName.textWithRenderedSymbolData( val actual = TestStructureRenderer.render(
path, fileStructure,
pointersWithRendered.joinToString(separator = "\n") { it.rendered }, TestStructureExpectedDataBlock(values = pointersWithRendered.map { it.rendered }),
renderingMode = TestStructureRenderer.RenderingMode.ALL_BLOCKS_IN_MULTI_LINE_COMMENT,
) )
KotlinTestUtils.assertEqualsToFile(File(path), actual) KotlinTestUtils.assertEqualsToFile(fileStructure.filePath.toFile(), actual)
doOutOfBlockModification(fileStructure.mainKtFile)
restoreSymbolsInOtherReadActionAndCompareResults(fileStructure, pointersWithRendered)
}
private fun restoreSymbolsInOtherReadActionAndCompareResults(
fileStructure: TestFileStructure,
pointersWithRendered: List<PointerWithRenderedSymbol>
) {
val restored = analyseOnPooledThreadInReadAction(fileStructure.mainKtFile) {
pointersWithRendered.map { (pointer, expectedRender) ->
val restored = pointer.restoreSymbol() ?: error("Symbol $expectedRender was not not restored correctly")
DebugSymbolRenderer.render(restored)
}
}
val actualRestored = TestStructureRenderer.render(
fileStructure,
TestStructureExpectedDataBlock(values = restored),
renderingMode = TestStructureRenderer.RenderingMode.ALL_BLOCKS_IN_MULTI_LINE_COMMENT,
)
KotlinTestUtils.assertEqualsToFile(fileStructure.filePath.toFile(), actualRestored)
}
private fun doOutOfBlockModification(ktFile: KtFile) {
CommandProcessor.getInstance().runUndoTransparentAction { CommandProcessor.getInstance().runUndoTransparentAction {
runWriteAction { runWriteAction {
KtPsiFactory(ktFile).apply { val document = PsiDocumentManager.getInstance(project).getDocument(ktFile) ?: error("Cannot find document for ktFile")
ktFile.add(createNewLine(lineBreaks = 2)) val initialText = ktFile.text
ktFile.add(createProperty("val aaaaaa: Int = 10")) val ktPsiFactory = KtPsiFactory(ktFile)
} ktFile.add(ktPsiFactory.createNewLine(lineBreaks = 2))
PsiDocumentManager.getInstance(project).apply { ktFile.add(ktPsiFactory.createProperty("val aaaaaa: Int = 10"))
commitDocument(getDocument(ktFile) ?: error("Cannot find document for ktFile")) commitDocument(document)
} document.setText(initialText)
commitDocument(document)
} }
} }
}
// another read action private fun commitDocument(document: Document) {
executeOnPooledThreadInReadAction { PsiDocumentManager.getInstance(project).commitDocument(document)
analyze(ktFile) {
val restored = pointersWithRendered.map { (pointer, expectedRender) ->
val restored = pointer.restoreSymbol() ?: error("Symbol $expectedRender was not not restored correctly")
DebugSymbolRenderer.render(restored)
}
val actualRestored = SymbolByFqName.textWithRenderedSymbolData(
path,
restored.joinToString(separator = "\n"),
)
KotlinTestUtils.assertEqualsToFile(File(path), actualRestored)
}
}
} }
} }
@@ -0,0 +1,41 @@
/*
* Copyright 2010-2021 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.idea.frontend.api.symbols;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/idea-frontend-fir/testData/symbolByReference")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class SymbolByReferencePointerRestoreTestGenerated extends AbstractSymbolByReferencePointerRestoreTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("accessorField.kt")
public void testAccessorField() throws Exception {
runTest("idea/idea-frontend-fir/testData/symbolByReference/accessorField.kt");
}
public void testAllFilesPresentInSymbolByReference() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/testData/symbolByReference"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("constructorViaTypeAlias.kt")
public void testConstructorViaTypeAlias() throws Exception {
runTest("idea/idea-frontend-fir/testData/symbolByReference/constructorViaTypeAlias.kt");
}
}
@@ -26,36 +26,36 @@ public class SymbolFromLibraryPointerRestoreTestGenerated extends AbstractSymbol
} }
public void testAllFilesPresentInResoreSymbolFromLibrary() throws Exception { public void testAllFilesPresentInResoreSymbolFromLibrary() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary"), Pattern.compile("^(.+)\\.txt$"), null, true); KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary"), Pattern.compile("^(.+)\\.kt$"), null, true);
} }
@TestMetadata("class.txt") @TestMetadata("class.kt")
public void testClass() throws Exception { public void testClass() throws Exception {
runTest("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary/class.txt"); runTest("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary/class.kt");
} }
@TestMetadata("classFromJdk.txt") @TestMetadata("classFromJdk.kt")
public void testClassFromJdk() throws Exception { public void testClassFromJdk() throws Exception {
runTest("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary/classFromJdk.txt"); runTest("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary/classFromJdk.kt");
} }
@TestMetadata("enumEntry.txt") @TestMetadata("enumEntry.kt")
public void testEnumEntry() throws Exception { public void testEnumEntry() throws Exception {
runTest("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary/enumEntry.txt"); runTest("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary/enumEntry.kt");
} }
@TestMetadata("memberFunction.txt") @TestMetadata("memberFunction.kt")
public void testMemberFunction() throws Exception { public void testMemberFunction() throws Exception {
runTest("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary/memberFunction.txt"); runTest("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary/memberFunction.kt");
} }
@TestMetadata("memberFunctionWithOverloads.txt") @TestMetadata("memberFunctionWithOverloads.kt")
public void testMemberFunctionWithOverloads() throws Exception { public void testMemberFunctionWithOverloads() throws Exception {
runTest("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary/memberFunctionWithOverloads.txt"); runTest("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary/memberFunctionWithOverloads.kt");
} }
@TestMetadata("nestedClass.txt") @TestMetadata("nestedClass.kt")
public void testNestedClass() throws Exception { public void testNestedClass() throws Exception {
runTest("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary/nestedClass.txt"); runTest("idea/idea-frontend-fir/testData/resoreSymbolFromLibrary/nestedClass.kt");
} }
} }
@@ -8,19 +8,25 @@ package org.jetbrains.kotlin.idea.test.framework
import org.jetbrains.kotlin.psi.psiUtil.getStartOffsetIn import org.jetbrains.kotlin.psi.psiUtil.getStartOffsetIn
object TestStructureRenderer { object TestStructureRenderer {
fun render(testStructure: TestFileStructure, vararg expectedData: TestStructureExpectedDataBlock): String = buildString { fun render(
testStructure: TestFileStructure,
vararg expectedData: TestStructureExpectedDataBlock,
renderingMode: RenderingMode = RenderingMode.EVERY_LINE_WITH_SINGLE_LINE_COMMENT,
): String =
render(testStructure, expectedData.toList(), renderingMode)
fun render(
testStructure: TestFileStructure,
expectedData: List<TestStructureExpectedDataBlock>,
renderingMode: RenderingMode = RenderingMode.EVERY_LINE_WITH_SINGLE_LINE_COMMENT
): String = buildString {
renderFiles(testStructure) renderFiles(testStructure)
renderExpectedData(expectedData.toList()) appendLine()
renderExpectedData(expectedData, renderingMode)
renderCaretSymbol(testStructure) renderCaretSymbol(testStructure)
renderExpressionTag(testStructure) renderExpressionTag(testStructure)
} }
fun render(testStructure: TestFileStructure, expectedData: List<TestStructureExpectedDataBlock>): String = buildString {
renderFiles(testStructure)
renderExpectedData(expectedData)
renderCaretSymbol(testStructure)
}
private fun StringBuilder.renderCaretSymbol(testStructure: TestFileStructure) { private fun StringBuilder.renderCaretSymbol(testStructure: TestFileStructure) {
testStructure.caretPosition?.let { position -> testStructure.caretPosition?.let { position ->
insert(position, KtTest.CARET_SYMBOL) insert(position, KtTest.CARET_SYMBOL)
@@ -45,29 +51,33 @@ object TestStructureRenderer {
} }
} }
private fun StringBuilder.renderExpectedData(expectedData: List<TestStructureExpectedDataBlock>) { private fun StringBuilder.renderExpectedData(expectedData: List<TestStructureExpectedDataBlock>, renderingMode: RenderingMode) {
if (expectedData.isNotEmpty()) { if (expectedData.isEmpty()) return
appendLine(KtTest.RESULT_DIRECTIVE) appendLine(KtTest.RESULT_DIRECTIVE)
appendLine() if (renderingMode == RenderingMode.ALL_BLOCKS_IN_MULTI_LINE_COMMENT) {
expectedData.forEach { block -> appendLine("/*")
renderExpectedDataBlock(block) }
expectedData.forEachIndexed { index, block ->
renderExpectedDataBlock(
block,
asSingleLineComment = renderingMode == RenderingMode.EVERY_LINE_WITH_SINGLE_LINE_COMMENT
)
if (index != expectedData.lastIndex) {
appendLine() appendLine()
} }
} }
if (renderingMode == RenderingMode.ALL_BLOCKS_IN_MULTI_LINE_COMMENT) {
appendLine("*/")
}
} }
private fun StringBuilder.renderExpectedDataBlock(block: TestStructureExpectedDataBlock) { private fun StringBuilder.renderExpectedDataBlock(block: TestStructureExpectedDataBlock, asSingleLineComment: Boolean) {
block.name?.let { name -> appendLine("// $name") } val singleLineCommentPrefix = if (asSingleLineComment) "// " else ""
block.values.forEach { value -> block.name?.let { name -> appendLine("$singleLineCommentPrefix$name") }
if (value.lines().size > 1) { block.values.forEachIndexed { index, value ->
appendLine( appendLine("$singleLineCommentPrefix${value.trim()}")
"""|/* if (index != block.values.lastIndex && !asSingleLineComment) {
|${value.trim()} appendLine()
|*/
""".trimMargin()
)
} else {
appendLine("// $value")
} }
} }
} }
@@ -77,4 +87,9 @@ object TestStructureRenderer {
appendLine(file.psiFile.text) appendLine(file.psiFile.text)
appendLine() appendLine()
} }
enum class RenderingMode {
EVERY_LINE_WITH_SINGLE_LINE_COMMENT,
ALL_BLOCKS_IN_MULTI_LINE_COMMENT
}
} }