FIR: Support DEBUG_INFO_CALL calls in tests
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
interface B {
|
||||
operator fun invoke(x: Int): String
|
||||
}
|
||||
class A {
|
||||
fun foo(x: Int) {}
|
||||
|
||||
val bar: B = TODO()
|
||||
}
|
||||
|
||||
fun A.foo(x: String) {}
|
||||
|
||||
fun main() {
|
||||
val a = A()
|
||||
a.<!DEBUG_INFO_CALL("fqName: A.foo; typeCall: function")!>foo(1)<!>
|
||||
a.<!DEBUG_INFO_CALL("fqName: foo; typeCall: extension function")!>foo("")<!>
|
||||
a.<!DEBUG_INFO_CALL("fqName: B.invoke; typeCall: variable&invoke")!>bar(1)<!>
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
FILE: debugInfoCall.kt
|
||||
public abstract interface B : R|kotlin/Any| {
|
||||
public abstract operator fun invoke(x: R|kotlin/Int|): R|kotlin/String|
|
||||
|
||||
}
|
||||
public final class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final fun foo(x: R|kotlin/Int|): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
public final val bar: R|B| = R|kotlin/TODO|()
|
||||
public get(): R|B|
|
||||
|
||||
}
|
||||
public final fun R|A|.foo(x: R|kotlin/String|): R|kotlin/Unit| {
|
||||
}
|
||||
public final fun main(): R|kotlin/Unit| {
|
||||
lval a: R|A| = R|/A.A|()
|
||||
R|<local>/a|.R|/A.foo|(Int(1))
|
||||
R|<local>/a|.R|/foo|(String())
|
||||
R|<local>/a|.R|/A.bar|.R|/B.invoke|(Int(1))
|
||||
}
|
||||
Generated
+5
@@ -584,6 +584,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/companionInvoke.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("debugInfoCall.kt")
|
||||
public void testDebugInfoCall() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/debugInfoCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("invokeAmbiguity.kt")
|
||||
public void testInvokeAmbiguity() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/invokeAmbiguity.kt");
|
||||
|
||||
+5
@@ -584,6 +584,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/companionInvoke.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("debugInfoCall.kt")
|
||||
public void testDebugInfoCall() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/debugInfoCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("invokeAmbiguity.kt")
|
||||
public void testInvokeAmbiguity() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/invokeAmbiguity.kt");
|
||||
|
||||
@@ -28,6 +28,10 @@ data class CallableId(val packageName: FqName, val className: FqName?, val calla
|
||||
@Deprecated("TODO: Better solution for local callables?")
|
||||
constructor(callableName: Name) : this(FqName.topLevel(Name.special("<local>")), null, callableName)
|
||||
|
||||
fun asFqName(): FqName {
|
||||
if (className == null) return packageName.child(callableName)
|
||||
return classId!!.asSingleFqName().child(callableName)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return buildString {
|
||||
|
||||
@@ -16,7 +16,7 @@ sealed class FirSourceElement {
|
||||
abstract val endOffset: Int
|
||||
}
|
||||
|
||||
class FirPsiSourceElement<P : PsiElement>(val psi: P) : FirSourceElement() {
|
||||
class FirPsiSourceElement<out P : PsiElement>(val psi: P) : FirSourceElement() {
|
||||
override val elementType: IElementType
|
||||
get() = psi.node.elementType
|
||||
|
||||
|
||||
@@ -27,7 +27,9 @@ import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.platform.isCommon
|
||||
import org.jetbrains.kotlin.platform.oldFashionedDescription
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils
|
||||
@@ -709,7 +711,7 @@ object CheckerTestUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private enum class TypeOfCall(val nameToRender: String) {
|
||||
enum class TypeOfCall(val nameToRender: String) {
|
||||
VARIABLE_THROUGH_INVOKE("variable&invoke"),
|
||||
PROPERTY_GETTER("variable"),
|
||||
FUNCTION("function"),
|
||||
|
||||
@@ -54,8 +54,8 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
|
||||
import org.jetbrains.kotlin.test.Directives
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils.isDirectiveDefined
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.KotlinBaseTest
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.junit.Assert
|
||||
import java.io.File
|
||||
@@ -141,7 +141,7 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
|
||||
directives: Directives
|
||||
) : KotlinBaseTest.TestFile(fileName, textWithMarkers, directives) {
|
||||
val diagnosedRanges: MutableList<DiagnosedRange> = mutableListOf()
|
||||
private val diagnosedRangesToDiagnosticNames: MutableMap<IntRange, MutableSet<String>> = mutableMapOf()
|
||||
val diagnosedRangesToDiagnosticNames: MutableMap<IntRange, MutableSet<String>> = mutableMapOf()
|
||||
val actualDiagnostics: MutableList<ActualDiagnostic> = mutableListOf()
|
||||
val expectedText: String
|
||||
val clearText: String
|
||||
|
||||
@@ -5,21 +5,39 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1
|
||||
import org.jetbrains.kotlin.checkers.utils.TypeOfCall
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
|
||||
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
|
||||
import org.jetbrains.kotlin.fir.analysis.collectors.FirDiagnosticsCollector
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
|
||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.FirControlFlowGraphReferenceImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.EdgeKind
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.FirControlFlowGraphRenderVisitor
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirTotalResolveTransformer
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.io.File
|
||||
|
||||
/*
|
||||
@@ -84,7 +102,12 @@ abstract class AbstractFirDiagnosticsTest : AbstractFirBaseDiagnosticsTest() {
|
||||
for (testFile in testFiles) {
|
||||
val firFile = firFiles.firstOrNull { it.psi == testFile.ktFile }
|
||||
if (firFile != null) {
|
||||
testFile.getActualText(diagnostics.getValue(firFile), actualText)
|
||||
val debugInfoDiagnostics: List<FirDiagnostic<*>> =
|
||||
collectDebugInfoDiagnostics(firFile, testFile.diagnosedRangesToDiagnosticNames)
|
||||
testFile.getActualText(
|
||||
diagnostics.getValue(firFile) + debugInfoDiagnostics,
|
||||
actualText,
|
||||
)
|
||||
} else {
|
||||
actualText.append(testFile.expectedText)
|
||||
}
|
||||
@@ -92,6 +115,106 @@ abstract class AbstractFirDiagnosticsTest : AbstractFirBaseDiagnosticsTest() {
|
||||
KotlinTestUtils.assertEqualsToFile(file, actualText.toString())
|
||||
}
|
||||
|
||||
protected fun collectDebugInfoDiagnostics(
|
||||
firFile: FirFile,
|
||||
diagnosedRangesToDiagnosticNames: MutableMap<IntRange, MutableSet<String>>
|
||||
): List<FirDiagnostic<*>> {
|
||||
val result = mutableListOf<FirDiagnostic<*>>()
|
||||
|
||||
|
||||
object : FirDefaultVisitorVoid() {
|
||||
override fun visitElement(element: FirElement) {
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
|
||||
override fun visitFunctionCall(functionCall: FirFunctionCall) {
|
||||
result.addIfNotNull(
|
||||
createCallDiagnosticIfExpected(functionCall, functionCall.calleeReference, diagnosedRangesToDiagnosticNames)
|
||||
)
|
||||
|
||||
super.visitFunctionCall(functionCall)
|
||||
}
|
||||
}.let(firFile::accept)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun createCallDiagnosticIfExpected(
|
||||
element: FirElement,
|
||||
reference: FirNamedReference,
|
||||
diagnosedRangesToDiagnosticNames: MutableMap<IntRange, MutableSet<String>>
|
||||
): FirDiagnosticWithParameters1<FirSourceElement, String>? {
|
||||
val resolvedSymbol = (reference as? FirResolvedNamedReference)?.resolvedSymbol
|
||||
val sourceElement = element.source ?: return null
|
||||
|
||||
val factory = DebugInfoDiagnosticFactory1.CALL
|
||||
if (diagnosedRangesToDiagnosticNames[sourceElement.startOffset..sourceElement.endOffset]?.contains(factory.name) != true) return null
|
||||
|
||||
val fqName = resolvedSymbol?.fqNameUnsafe()
|
||||
|
||||
val argument = Renderers.renderCallInfo(fqName, getTypeOfCall(reference, resolvedSymbol))
|
||||
|
||||
return when (sourceElement) {
|
||||
is FirPsiSourceElement<*> -> FirPsiDiagnosticWithParameters1(
|
||||
sourceElement,
|
||||
argument,
|
||||
factory.severity,
|
||||
FirDiagnosticFactory1(
|
||||
factory.name,
|
||||
factory.severity,
|
||||
factory
|
||||
)
|
||||
)
|
||||
is FirLightSourceElement -> FirLightDiagnosticWithParameters1(
|
||||
sourceElement,
|
||||
argument,
|
||||
factory.severity,
|
||||
FirDiagnosticFactory1<FirSourceElement, PsiElement, String>(
|
||||
factory.name,
|
||||
factory.severity,
|
||||
factory
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun AbstractFirBasedSymbol<*>.fqNameUnsafe(): FqNameUnsafe? = when (this) {
|
||||
is FirClassLikeSymbol<*> -> classId.asSingleFqName().toUnsafe()
|
||||
is FirCallableSymbol<*> -> callableId.asFqName().toUnsafe()
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun getTypeOfCall(
|
||||
reference: FirNamedReference,
|
||||
resolvedSymbol: AbstractFirBasedSymbol<*>?
|
||||
): String {
|
||||
if (resolvedSymbol == null) return TypeOfCall.UNRESOLVED.nameToRender
|
||||
|
||||
if ((resolvedSymbol as? FirFunctionSymbol)?.callableId?.callableName == OperatorNameConventions.INVOKE
|
||||
&& reference.name != OperatorNameConventions.INVOKE
|
||||
) {
|
||||
return TypeOfCall.VARIABLE_THROUGH_INVOKE.nameToRender
|
||||
}
|
||||
|
||||
return when (val fir = resolvedSymbol.fir) {
|
||||
is FirProperty -> {
|
||||
TypeOfCall.PROPERTY_GETTER.nameToRender
|
||||
}
|
||||
is FirFunction<*> -> buildString {
|
||||
if (fir is FirCallableMemberDeclaration<*>) {
|
||||
if (fir.status.isInline) append("inline ")
|
||||
if (fir.status.isInfix) append("infix ")
|
||||
if (fir.status.isOperator) append("operator ")
|
||||
if (fir.receiverTypeRef != null) append("extension ")
|
||||
}
|
||||
append(TypeOfCall.FUNCTION.nameToRender)
|
||||
}
|
||||
else -> TypeOfCall.OTHER.nameToRender
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected fun collectDiagnostics(firFiles: List<FirFile>): Map<FirFile, List<FirDiagnostic<*>>> {
|
||||
val collectors = mutableMapOf<FirSession, AbstractDiagnosticCollector>()
|
||||
val result = mutableMapOf<FirFile, List<FirDiagnostic<*>>>()
|
||||
@@ -165,4 +288,4 @@ abstract class AbstractFirDiagnosticsTest : AbstractFirBaseDiagnosticsTest() {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-3
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirLightDiagnostic
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
@@ -33,7 +34,11 @@ abstract class AbstractFirDiagnosticsWithLightTreeTest : AbstractFirDiagnosticsT
|
||||
for (testFile in testFiles) {
|
||||
val firFile = firFiles.firstOrNull { it.name == testFile.name } ?: continue
|
||||
val ktFile = testFile.ktFile!!
|
||||
val diagnostics = fileToDiagnostics[firFile] ?: emptyList()
|
||||
val basicDiagnostics = fileToDiagnostics[firFile] ?: emptyList()
|
||||
val debugInfoDiagnostics: List<FirDiagnostic<*>> =
|
||||
collectDebugInfoDiagnostics(firFile, testFile.diagnosedRangesToDiagnosticNames)
|
||||
|
||||
val diagnostics = basicDiagnostics + debugInfoDiagnostics
|
||||
|
||||
val actualDiagnostics = diagnostics.groupBy {
|
||||
require(it is FirLightDiagnostic)
|
||||
@@ -42,7 +47,11 @@ abstract class AbstractFirDiagnosticsWithLightTreeTest : AbstractFirDiagnosticsT
|
||||
|
||||
val existingDiagnostics = testFile.diagnosedRanges.groupBy {
|
||||
it.start
|
||||
}.mapValues { (_, ranges) -> ranges.flatMap { it.getDiagnostics().map { it.name } }.countEntries() }
|
||||
}.mapValues { (_, ranges) ->
|
||||
ranges.flatMap { diagnosedRange ->
|
||||
diagnosedRange.getDiagnostics().filterNot { it.name.contains("DEBUG_INFO_") }.map { it.name }
|
||||
}.countEntries()
|
||||
}
|
||||
|
||||
for (startOffset in actualDiagnostics.keys + existingDiagnostics.keys) {
|
||||
val expected = existingDiagnostics[startOffset] ?: emptyMap()
|
||||
@@ -102,4 +111,4 @@ abstract class AbstractFirDiagnosticsWithLightTreeTest : AbstractFirDiagnosticsT
|
||||
IsActual
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user