From f7ff60cb77cb787a87571c1f90369a9052c06324 Mon Sep 17 00:00:00 2001 From: "anastasiia.spaseeva" Date: Thu, 19 Mar 2020 15:13:32 +0300 Subject: [PATCH] [Spec tests] Add DEBUG_INFO_AS_CALL diagnostic tag for callable references --- .../factories/DebugInfoDiagnosticFactory1.kt | 11 +- .../kotlin/checkers/utils/CheckerTestUtil.kt | 193 +++++++++++++++--- .../kotlin/diagnostics/rendering/Renderers.kt | 8 + .../kotlin/resolve/BindingContext.java | 1 + 4 files changed, 188 insertions(+), 25 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/checkers/diagnostics/factories/DebugInfoDiagnosticFactory1.kt b/compiler/frontend/src/org/jetbrains/kotlin/checkers/diagnostics/factories/DebugInfoDiagnosticFactory1.kt index 7219bafcb65..64e8e4b6278 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/checkers/diagnostics/factories/DebugInfoDiagnosticFactory1.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/checkers/diagnostics/factories/DebugInfoDiagnosticFactory1.kt @@ -34,7 +34,7 @@ class DebugInfoDiagnosticFactory1 : DiagnosticFactory1, languageVersionSettings: LanguageVersionSettings?, moduleDescriptor: ModuleDescriptorImpl? ) = when (name) { - "EXPRESSION_TYPE" -> { + EXPRESSION_TYPE.name -> { val (type, dataFlowTypes) = CheckerTestUtil.getTypeInfo( expression, bindingContext, @@ -45,6 +45,10 @@ class DebugInfoDiagnosticFactory1 : DiagnosticFactory1, this.on(expression, Renderers.renderExpressionType(type, dataFlowTypes)) } + CALL.name -> { + val (fqName, typeCall) = CheckerTestUtil.getCallDebugInfo(expression, bindingContext) + this.on(expression, Renderers.renderCallInfo(fqName, typeCall)) + } else -> throw NotImplementedError("Creation diagnostic '$name' isn't supported.") } @@ -67,6 +71,11 @@ class DebugInfoDiagnosticFactory1 : DiagnosticFactory1, Severity.INFO, true ) + val CALL = create( + "CALL", + Severity.INFO, + true + ) fun create(name: String, severity: Severity): DebugInfoDiagnosticFactory1 { return DebugInfoDiagnosticFactory1(name, severity) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/checkers/utils/CheckerTestUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/checkers/utils/CheckerTestUtil.kt index 9bd7f2ab241..0cc8182e41b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/checkers/utils/CheckerTestUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/checkers/utils/CheckerTestUtil.kt @@ -19,21 +19,31 @@ import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFa import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.diagnostics.DiagnosticFactoryWithPsiElement +import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.platform.TargetPlatform -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.* -import org.jetbrains.kotlin.resolve.calls.callUtil.getType -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.platform.isCommon import org.jetbrains.kotlin.platform.oldFashionedDescription +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.AnalyzingUtils +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getCall +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.callUtil.getType +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo +import org.jetbrains.kotlin.util.slicedMap.WritableSlice import java.util.* import java.util.regex.Pattern @@ -176,27 +186,115 @@ object CheckerTestUtil { BindingContext.IMPLICIT_EXHAUSTIVE_WHEN to DebugInfoDiagnosticFactory0.IMPLICIT_EXHAUSTIVE ) + val kotlinSpecificInfo = KotlinSpecificInfo( + platform, + withNewInference, + languageVersionSettings, + ) + /** for expressions*/ for ((context, factory) in factoryList) { - for ((expression, _) in bindingContext.getSliceContents(context)) { - val needRender = !factory.withExplicitDefinitionOnly - || diagnosedRanges?.get(expression.startOffset..expression.endOffset)?.contains(factory.name) == true - - if (PsiTreeUtil.isAncestor(root, expression, false) && needRender) { - val diagnostic = factory.createDiagnostic( - expression, - bindingContext, - dataFlowValueFactory, - languageVersionSettings, - moduleDescriptor - ) - debugAnnotations.add(ActualDiagnostic(diagnostic, platform, withNewInference)) - } - } + renderDiagnosticsForExpressions( + context, + factory, + root, + bindingContext, + kotlinSpecificInfo, + dataFlowValueFactory, + moduleDescriptor, + diagnosedRanges, + debugAnnotations + ) } + + /** for calls*/ + renderDiagnosticsForCalls( + BindingContext.RESOLVED_CALL, + DebugInfoDiagnosticFactory1.CALL, + root, + bindingContext, + kotlinSpecificInfo, + dataFlowValueFactory, + moduleDescriptor, + diagnosedRanges, + debugAnnotations + ) + return debugAnnotations } + private class KotlinSpecificInfo( + val platform: String?, + val withNewInference: Boolean, + val languageVersionSettings: LanguageVersionSettings? + ) + + private fun renderDiagnosticsForCalls( + context: WritableSlice>, callFactory: DebugInfoDiagnosticFactory1, + root: PsiElement, bindingContext: BindingContext, kotlinSpecificInfo: KotlinSpecificInfo, + dataFlowValueFactory: DataFlowValueFactory?, + moduleDescriptor: ModuleDescriptorImpl?, + diagnosedRanges: Map>?, + + debugAnnotations: MutableList + ) { + for ((call, _) in bindingContext.getSliceContents(context)) { + val callElement = call.callElement as? KtExpression ?: continue + renderDiagnostics( + callFactory, callElement, + root, bindingContext, kotlinSpecificInfo, dataFlowValueFactory, moduleDescriptor, diagnosedRanges, + debugAnnotations + ) + } + } + + private fun renderDiagnosticsForExpressions( + context: WritableSlice, factory: DebugInfoDiagnosticFactory, + root: PsiElement, + bindingContext: BindingContext, + kotlinSpecificInfo: KotlinSpecificInfo, + + dataFlowValueFactory: DataFlowValueFactory?, + moduleDescriptor: ModuleDescriptorImpl?, + diagnosedRanges: Map>?, + + debugAnnotations: MutableList + ) { + + for ((expression, _) in bindingContext.getSliceContents(context)) { + renderDiagnostics( + factory, expression, + root, bindingContext, kotlinSpecificInfo, dataFlowValueFactory, moduleDescriptor, diagnosedRanges, + debugAnnotations + ) + } + } + + private fun renderDiagnostics( + callFactory: DebugInfoDiagnosticFactory, callElement: KtExpression, + root: PsiElement, + bindingContext: BindingContext, + kotlinSpecificInfo: KotlinSpecificInfo, + dataFlowValueFactory: DataFlowValueFactory?, + moduleDescriptor: ModuleDescriptorImpl?, + diagnosedRanges: Map>?, + + debugAnnotations: MutableList + ) { + (callFactory as DiagnosticFactoryWithPsiElement<*, *>) + val needRender = !callFactory.withExplicitDefinitionOnly + || diagnosedRanges?.get(callElement.startOffset..callElement.endOffset)?.contains(callFactory.name) == true + if (PsiTreeUtil.isAncestor(root, callElement, false) && needRender) { + val diagnostic = callFactory.createDiagnostic( + callElement, + bindingContext, + dataFlowValueFactory, + kotlinSpecificInfo.languageVersionSettings, + moduleDescriptor + ) + debugAnnotations.add(ActualDiagnostic(diagnostic, kotlinSpecificInfo.platform, kotlinSpecificInfo.withNewInference)) + } + } fun diagnosticsDiff( expected: List, @@ -627,4 +725,51 @@ object CheckerTestUtil { } return Pair(result, null) } + + fun getCallDebugInfo( + expression: PsiElement, + bindingContext: BindingContext, + ): Pair { + val callToTypeOfCall = + when (expression) { + is KtCallableReferenceExpression -> { + expression.callableReference.getCall(bindingContext) to getTypeOfCall(expression, bindingContext) + } + is KtExpression -> { + expression.getCall(bindingContext) to getTypeOfCall(expression, bindingContext) + } + else -> null + } + val fqNameUnsafe = bindingContext[BindingContext.RESOLVED_CALL, callToTypeOfCall?.first]?.candidateDescriptor?.fqNameUnsafe + return fqNameUnsafe to callToTypeOfCall?.second + + } + + /** + * if resolvedCall is VariableAsFunctionResolvedCall -- then typeOfCall is variable + invoke + * if resolvedCall.candidateDescriptor is PropertyDescriptor -- then typeOfCall is variable + * if resolvedCall.candidateDescriptor is FunctionDescriptor -- then typeOfCall is function + */ + fun getTypeOfCall(expression: KtExpression, bindingContext: BindingContext): String? { + val resolvedCall = expression.getResolvedCall(bindingContext) ?: return null + return if (resolvedCall is VariableAsFunctionResolvedCall) { + "variable&invoke" + } else { + when (val functionDescriptor = resolvedCall.candidateDescriptor) { + is PropertyDescriptor -> { + "variable" + } + is FunctionDescriptor -> { + return buildString { + if (functionDescriptor.isInline) append("inline ") + if (functionDescriptor.isInfix) append("infix ") + if (functionDescriptor.isOperator) append("operator ") + if (functionDescriptor.isExtension) append("extension ") + append("function") + } + } + else -> null + } + } + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt index fb49caa3beb..b98a8d8e2c5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newTable import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newText import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.isCommon import org.jetbrains.kotlin.psi.* @@ -738,6 +739,13 @@ object Renderers { return typesAsString.sorted().joinToString(separator = " & ") } + + fun renderCallInfo(fqName: FqNameUnsafe?, typeCall: String?): String { + val info = java.lang.StringBuilder() + info.append("fqName: ${fqName?.asString() ?: "fqName is unknown"}; ") + info.append("typeCall: ${typeCall ?: "typeCall is unknown"}") + return info.toString() + } } fun DescriptorRenderer.asRenderer() = SmartDescriptorRenderer(this) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index d694904da69..d337f776b7f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -96,6 +96,7 @@ public interface BindingContext { WritableSlice TYPE = Slices.createSimpleSlice(); WritableSlice ABBREVIATED_TYPE = Slices.createSimpleSlice(); WritableSlice EXPRESSION_TYPE_INFO = new BasicWritableSlice<>(DO_NOTHING); + WritableSlice FQ_NAME = new BasicWritableSlice<>(DO_NOTHING); WritableSlice DATA_FLOW_INFO_BEFORE = new BasicWritableSlice<>(DO_NOTHING); WritableSlice EXPECTED_EXPRESSION_TYPE = new BasicWritableSlice<>(DO_NOTHING); WritableSlice EXPRESSION_EFFECTS = Slices.createSimpleSlice();