diff --git a/compiler/fir/analysis-tests/testData/resolve/callResolution/debugExpressionType.kt b/compiler/fir/analysis-tests/testData/resolve/callResolution/debugExpressionType.kt new file mode 100644 index 00000000000..8181bf00476 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/callResolution/debugExpressionType.kt @@ -0,0 +1,9 @@ +fun foo( + x1: String, + x2: Collection, + x3: MutableMap> +) { + x1 + ")!>x2 + >")!>x3 +} diff --git a/compiler/fir/analysis-tests/testData/resolve/callResolution/debugExpressionType.txt b/compiler/fir/analysis-tests/testData/resolve/callResolution/debugExpressionType.txt new file mode 100644 index 00000000000..d14384c3e41 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/callResolution/debugExpressionType.txt @@ -0,0 +1,6 @@ +FILE: debugExpressionType.kt + public final fun foo(x1: R|kotlin/String|, x2: R|kotlin/collections/Collection|, x3: R|kotlin/collections/MutableMap>|): R|kotlin/Unit| { + R|/x1| + R|/x2| + R|/x3| + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index e85b2b31546..4a8b9fa6145 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -584,6 +584,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/companionInvoke.kt"); } + @TestMetadata("debugExpressionType.kt") + public void testDebugExpressionType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/debugExpressionType.kt"); + } + @TestMetadata("debugInfoCall.kt") public void testDebugInfoCall() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/debugInfoCall.kt"); diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java index 5d7f9fc822b..f7c43f130d4 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java @@ -584,6 +584,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/companionInvoke.kt"); } + @TestMetadata("debugExpressionType.kt") + public void testDebugExpressionType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/debugExpressionType.kt"); + } + @TestMetadata("debugInfoCall.kt") public void testDebugInfoCall() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/debugInfoCall.kt"); diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirDiagnosticTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirDiagnosticTest.kt index 4f5fcad1bb9..494e68c624f 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirDiagnosticTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractFirDiagnosticTest.kt @@ -17,6 +17,8 @@ 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.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference import org.jetbrains.kotlin.fir.references.FirNamedReference @@ -31,6 +33,9 @@ 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.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.fir.types.coneTypeSafe import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.name.FqNameUnsafe @@ -124,6 +129,14 @@ abstract class AbstractFirDiagnosticsTest : AbstractFirBaseDiagnosticsTest() { object : FirDefaultVisitorVoid() { override fun visitElement(element: FirElement) { + if (element is FirExpression) { + result.addIfNotNull( + createExpressionTypeDiagnosticIfExpected( + element, diagnosedRangesToDiagnosticNames + ) + ) + } + element.acceptChildren(this) } @@ -139,46 +152,67 @@ abstract class AbstractFirDiagnosticsTest : AbstractFirBaseDiagnosticsTest() { return result } - fun createCallDiagnosticIfExpected( + fun createExpressionTypeDiagnosticIfExpected( + element: FirExpression, + diagnosedRangesToDiagnosticNames: MutableMap> + ): FirDiagnosticWithParameters1? = + DebugInfoDiagnosticFactory1.EXPRESSION_TYPE.createDebugInfoDiagnostic(element, diagnosedRangesToDiagnosticNames) { + element.typeRef.renderAsString((element as? FirExpressionWithSmartcast)?.originalType) + } + + private fun FirTypeRef.renderAsString(originalTypeRef: FirTypeRef?): String { + val type = coneTypeSafe() ?: return "Type is unknown" + val rendered = type.renderForDebugInfo() + val originalTypeRendered = originalTypeRef?.coneTypeSafe()?.renderForDebugInfo() ?: return rendered + + return "$rendered & $originalTypeRendered" + } + + private fun createCallDiagnosticIfExpected( element: FirElement, reference: FirNamedReference, diagnosedRangesToDiagnosticNames: MutableMap> + ): FirDiagnosticWithParameters1? = + DebugInfoDiagnosticFactory1.CALL.createDebugInfoDiagnostic(element, diagnosedRangesToDiagnosticNames) { + + val resolvedSymbol = (reference as? FirResolvedNamedReference)?.resolvedSymbol + val fqName = resolvedSymbol?.fqNameUnsafe() + Renderers.renderCallInfo(fqName, getTypeOfCall(reference, resolvedSymbol)) + } + + private fun DebugInfoDiagnosticFactory1.createDebugInfoDiagnostic( + element: FirElement, + diagnosedRangesToDiagnosticNames: MutableMap>, + argument: () -> String, ): FirDiagnosticWithParameters1? { - val resolvedSymbol = (reference as? FirResolvedNamedReference)?.resolvedSymbol val sourceElement = element.source ?: return null + if (diagnosedRangesToDiagnosticNames[sourceElement.startOffset..sourceElement.endOffset]?.contains(this.name) != true) 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)) - + val argumentText = argument() return when (sourceElement) { is FirPsiSourceElement<*> -> FirPsiDiagnosticWithParameters1( sourceElement, - argument, - factory.severity, + argumentText, + severity, FirDiagnosticFactory1( - factory.name, - factory.severity, - factory + name, + severity, + this ) ) is FirLightSourceElement -> FirLightDiagnosticWithParameters1( sourceElement, - argument, - factory.severity, + argumentText, + severity, FirDiagnosticFactory1( - factory.name, - factory.severity, - factory + name, + severity, + this ) ) } } - private fun AbstractFirBasedSymbol<*>.fqNameUnsafe(): FqNameUnsafe? = when (this) { is FirClassLikeSymbol<*> -> classId.asSingleFqName().toUnsafe() is FirCallableSymbol<*> -> callableId.asFqName().toUnsafe() diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/RenderingForDebugInfo.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/RenderingForDebugInfo.kt new file mode 100644 index 00000000000..c4a2f0459fa --- /dev/null +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/RenderingForDebugInfo.kt @@ -0,0 +1,54 @@ +/* + * 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.fir + +import org.jetbrains.kotlin.fir.types.* + +fun ConeKotlinType.renderForDebugInfo(): String { + val nullabilitySuffix = if (this !is ConeKotlinErrorType && this !is ConeClassErrorType) nullability.suffix else "" + return when (this) { + is ConeTypeVariableType -> "TypeVariable(${this.lookupTag.name})" + is ConeDefinitelyNotNullType -> "${original.renderForDebugInfo()}!!" + is ConeClassErrorType -> "ERROR CLASS: $reason" + is ConeCapturedType -> "CapturedType(${constructor.projection.renderForDebugInfo()})" + is ConeClassLikeType -> { + buildString { + append(lookupTag.classId.asSingleFqName().asString()) + if (typeArguments.isNotEmpty()) { + append(typeArguments.joinToString(prefix = "<", postfix = ">") { + it.renderForDebugInfo() + }) + } + } + } + is ConeLookupTagBasedType -> { + lookupTag.name.asString() + } + is ConeFlexibleType -> { + buildString { + append(lowerBound.renderForDebugInfo()) + append("..") + append(upperBound.renderForDebugInfo()) + } + } + is ConeIntersectionType -> { + intersectedTypes.joinToString( + separator = " & ", + ) { it.renderForDebugInfo() } + } + is ConeStubType -> "Stub: $variable" + is ConeIntegerLiteralType -> "ILT: $value" + } + nullabilitySuffix +} + +private fun ConeTypeProjection.renderForDebugInfo(): String { + return when (this) { + ConeStarProjection -> "*" + is ConeKotlinTypeProjectionIn -> "in ${type.renderForDebugInfo()}" + is ConeKotlinTypeProjectionOut -> "out ${type.renderForDebugInfo()}" + is ConeKotlinType -> renderForDebugInfo() + } +}