FIR: Support DEBUG_INFO_CALL calls in tests

This commit is contained in:
Denis Zharkov
2020-04-08 17:03:06 +03:00
parent 22dc837e26
commit b62400124a
10 changed files with 201 additions and 11 deletions
@@ -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() {
}
}
}
}
@@ -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
}
}
}
}