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 0cc8182e41b..6b9979cef4a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/checkers/utils/CheckerTestUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/checkers/utils/CheckerTestUtil.kt @@ -22,7 +22,7 @@ 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.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.isCommon @@ -35,7 +35,6 @@ 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 @@ -47,6 +46,12 @@ import org.jetbrains.kotlin.util.slicedMap.WritableSlice import java.util.* import java.util.regex.Pattern +data class DiagnosticsRenderingConfiguration( + val platform: String?, + val withNewInference: Boolean, + val languageVersionSettings: LanguageVersionSettings? +) + object CheckerTestUtil { const val NEW_INFERENCE_PREFIX = "NI" const val OLD_INFERENCE_PREFIX = "OI" @@ -63,8 +68,7 @@ object CheckerTestUtil { root: PsiElement, markDynamicCalls: Boolean, dynamicCallDescriptors: MutableList, - withNewInference: Boolean, - languageVersionSettings: LanguageVersionSettings, + configuration: DiagnosticsRenderingConfiguration, dataFlowValueFactory: DataFlowValueFactory?, moduleDescriptor: ModuleDescriptorImpl?, diagnosedRanges: MutableMap>? = null @@ -74,9 +78,7 @@ object CheckerTestUtil { root, markDynamicCalls, dynamicCallDescriptors, - null, - withNewInference, - languageVersionSettings, + configuration, dataFlowValueFactory, moduleDescriptor, diagnosedRanges @@ -93,9 +95,7 @@ object CheckerTestUtil { root, markDynamicCalls, dynamicCallDescriptors, - platform.single().platformName, - withNewInference, - languageVersionSettings, + configuration.copy(platform = platform.single().platformName), dataFlowValueFactory, moduleDescriptor, diagnosedRanges @@ -111,9 +111,7 @@ object CheckerTestUtil { root: PsiElement, markDynamicCalls: Boolean, dynamicCallDescriptors: MutableList, - platform: String?, - withNewInference: Boolean, - languageVersionSettings: LanguageVersionSettings?, + configuration: DiagnosticsRenderingConfiguration, dataFlowValueFactory: DataFlowValueFactory?, moduleDescriptor: ModuleDescriptorImpl?, diagnosedRanges: MutableMap>? = null @@ -122,12 +120,12 @@ object CheckerTestUtil { bindingContext.diagnostics.forEach { diagnostic -> if (PsiTreeUtil.isAncestor(root, diagnostic.psiElement, false)) { - diagnostics.add(ActualDiagnostic(diagnostic, platform, withNewInference)) + diagnostics.add(ActualDiagnostic(diagnostic, configuration.platform, configuration.withNewInference)) } } for (errorElement in AnalyzingUtils.getSyntaxErrorRanges(root)) { - diagnostics.add(ActualDiagnostic(SyntaxErrorDiagnostic(errorElement), platform, withNewInference)) + diagnostics.add(ActualDiagnostic(SyntaxErrorDiagnostic(errorElement), configuration.platform, configuration.withNewInference)) } diagnostics.addAll( @@ -136,9 +134,7 @@ object CheckerTestUtil { bindingContext, markDynamicCalls, dynamicCallDescriptors, - platform, - withNewInference, - languageVersionSettings, + configuration, dataFlowValueFactory, moduleDescriptor, diagnosedRanges @@ -153,9 +149,7 @@ object CheckerTestUtil { bindingContext: BindingContext, markDynamicCalls: Boolean, dynamicCallDescriptors: MutableList, - platform: String?, - withNewInference: Boolean, - languageVersionSettings: LanguageVersionSettings?, + configuration: DiagnosticsRenderingConfiguration, dataFlowValueFactory: DataFlowValueFactory?, moduleDescriptor: ModuleDescriptorImpl?, diagnosedRanges: Map>? @@ -169,15 +163,15 @@ object CheckerTestUtil { dynamicCallDescriptors, markDynamicCalls, debugAnnotations, - withNewInference, - platform + configuration.withNewInference, + configuration.platform ) ) // this code is used in tests and in internal action 'copy current file as diagnostic test' //noinspection unchecked - val factoryList = listOf( + val factoryListForDiagnosticsOnExpression = listOf( BindingContext.EXPRESSION_TYPE_INFO to DebugInfoDiagnosticFactory1.EXPRESSION_TYPE, BindingContext.SMARTCAST to DebugInfoDiagnosticFactory0.SMARTCAST, BindingContext.IMPLICIT_RECEIVER_SMARTCAST to DebugInfoDiagnosticFactory0.IMPLICIT_RECEIVER_SMARTCAST, @@ -186,113 +180,69 @@ object CheckerTestUtil { BindingContext.IMPLICIT_EXHAUSTIVE_WHEN to DebugInfoDiagnosticFactory0.IMPLICIT_EXHAUSTIVE ) - val kotlinSpecificInfo = KotlinSpecificInfo( - platform, - withNewInference, - languageVersionSettings, - ) - /** for expressions*/ - for ((context, factory) in factoryList) { - renderDiagnosticsForExpressions( - context, - factory, - root, - bindingContext, - kotlinSpecificInfo, - dataFlowValueFactory, - moduleDescriptor, - diagnosedRanges, - debugAnnotations - ) - } + val factoryListForDiagnosticsOnCall = listOf(BindingContext.RESOLVED_CALL to DebugInfoDiagnosticFactory1.CALL) - - /** for calls*/ - renderDiagnosticsForCalls( - BindingContext.RESOLVED_CALL, - DebugInfoDiagnosticFactory1.CALL, - root, - bindingContext, - kotlinSpecificInfo, - dataFlowValueFactory, - moduleDescriptor, - diagnosedRanges, - debugAnnotations + renderDiagnosticsByFactoryList( + factoryListForDiagnosticsOnExpression, root, bindingContext, configuration, + dataFlowValueFactory, moduleDescriptor, diagnosedRanges, debugAnnotations ) + renderDiagnosticsByFactoryList( + factoryListForDiagnosticsOnCall, root, bindingContext, configuration, + dataFlowValueFactory, moduleDescriptor, diagnosedRanges, debugAnnotations + ) { it.callElement as? KtExpression } + 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, + private fun renderDiagnosticsByFactoryList( + factoryList: List, DebugInfoDiagnosticFactory>>, root: PsiElement, bindingContext: BindingContext, - kotlinSpecificInfo: KotlinSpecificInfo, - + configuration: DiagnosticsRenderingConfiguration, dataFlowValueFactory: DataFlowValueFactory?, moduleDescriptor: ModuleDescriptorImpl?, diagnosedRanges: Map>?, - - debugAnnotations: MutableList + debugAnnotations: MutableList, + toExpression: (T) -> KtExpression? = { it as? KtExpression } ) { - - for ((expression, _) in bindingContext.getSliceContents(context)) { - renderDiagnostics( - factory, expression, - root, bindingContext, kotlinSpecificInfo, dataFlowValueFactory, moduleDescriptor, diagnosedRanges, - debugAnnotations - ) + for ((context, factory) in factoryList) { + for ((element, _) in bindingContext.getSliceContents(context)) { + renderDiagnostics( + factory, + toExpression(element) ?: continue, + root, bindingContext, configuration, dataFlowValueFactory, moduleDescriptor, diagnosedRanges, + debugAnnotations + ) + } } } private fun renderDiagnostics( - callFactory: DebugInfoDiagnosticFactory, callElement: KtExpression, + factory: DebugInfoDiagnosticFactory, + expression: KtExpression, root: PsiElement, bindingContext: BindingContext, - kotlinSpecificInfo: KotlinSpecificInfo, + configuration: DiagnosticsRenderingConfiguration, 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, + if (factory !is DiagnosticFactory<*>) return + + 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, - kotlinSpecificInfo.languageVersionSettings, + configuration.languageVersionSettings, moduleDescriptor ) - debugAnnotations.add(ActualDiagnostic(diagnostic, kotlinSpecificInfo.platform, kotlinSpecificInfo.withNewInference)) + debugAnnotations.add(ActualDiagnostic(diagnostic, configuration.platform, configuration.withNewInference)) } } @@ -726,50 +676,43 @@ 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 + fun getCallDebugInfo(element: PsiElement, bindingContext: BindingContext): Pair { + if (element !is KtExpression) + return null to TypeOfCall.OTHER.nameToRender + val call = element.getCall(bindingContext) + val typeOfCall = getTypeOfCall(element, bindingContext) + val fqNameUnsafe = bindingContext[BindingContext.RESOLVED_CALL, call]?.candidateDescriptor?.fqNameUnsafe + + return fqNameUnsafe to typeOfCall } - /** - * 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 + private fun getTypeOfCall(expression: KtExpression, bindingContext: BindingContext): String { + val resolvedCall = expression.getResolvedCall(bindingContext) ?: return TypeOfCall.UNRESOLVED.nameToRender + + if (resolvedCall is VariableAsFunctionResolvedCall) + return TypeOfCall.VARIABLE_THROUGH_INVOKE.nameToRender + + return when (val functionDescriptor = resolvedCall.candidateDescriptor) { + is PropertyDescriptor -> { + TypeOfCall.PROPERTY_GETTER.nameToRender } + is FunctionDescriptor -> buildString { + if (functionDescriptor.isInline) append("inline ") + if (functionDescriptor.isInfix) append("infix ") + if (functionDescriptor.isOperator) append("operator ") + if (functionDescriptor.isExtension) append("extension ") + append(TypeOfCall.FUNCTION.nameToRender) + } + else -> TypeOfCall.OTHER.nameToRender } } } + +private enum class TypeOfCall(val nameToRender: String) { + VARIABLE_THROUGH_INVOKE("variable&invoke"), + PROPERTY_GETTER("variable"), + FUNCTION("function"), + UNRESOLVED("unresolved"), + OTHER("other") +} 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 b98a8d8e2c5..b9cdb6c1f32 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt @@ -740,12 +740,11 @@ 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 renderCallInfo(fqName: FqNameUnsafe?, typeCall: String) = + buildString { + append("fqName: ${fqName?.asString() ?: "fqName is unknown"}; ") + append("typeCall: $typeCall") + } } 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 d337f776b7f..d694904da69 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -96,7 +96,6 @@ 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(); diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/BaseDiagnosticsTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/BaseDiagnosticsTest.kt index 5418ac098c0..5456b5e1b75 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/BaseDiagnosticsTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/BaseDiagnosticsTest.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.checkers.diagnostics.TextDiagnostic import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory0 import org.jetbrains.kotlin.checkers.diagnostics.factories.SyntaxErrorDiagnosticFactory import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil +import org.jetbrains.kotlin.checkers.utils.DiagnosticsRenderingConfiguration import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.config.LanguageFeature @@ -260,8 +261,11 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava String = {""} ): String = body() - s?.orEmpty() + s?.orEmpty() //trailing lambda s.funWithTrailingLambda { "ss" } s.funWithTrailingLambda (x= 1) { "ss" } diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/internal/CopyAsDiagnosticTestAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/internal/CopyAsDiagnosticTestAction.kt index 45d16f2f810..628ed900421 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/internal/CopyAsDiagnosticTestAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/internal/CopyAsDiagnosticTestAction.kt @@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.application.ApplicationManager import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil +import org.jetbrains.kotlin.checkers.utils.DiagnosticsRenderingConfiguration import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent import org.jetbrains.kotlin.psi.KtFile import java.awt.Toolkit @@ -40,9 +41,7 @@ class CopyAsDiagnosticTestAction : AnAction() { psiFile, false, mutableListOf(), - null, - false, - null, + DiagnosticsRenderingConfiguration(null, false, null), null, null ) diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractMultiModuleIdeResolveTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractMultiModuleIdeResolveTest.kt index 741b7aa1af9..194d2bcfda0 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractMultiModuleIdeResolveTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractMultiModuleIdeResolveTest.kt @@ -12,6 +12,7 @@ import com.intellij.psi.PsiManager import com.intellij.util.io.exists import org.jetbrains.kotlin.checkers.BaseDiagnosticsTest import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil +import org.jetbrains.kotlin.checkers.utils.DiagnosticsRenderingConfiguration import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.idea.multiplatform.setupMppProjectFromTextFile import org.jetbrains.kotlin.idea.project.KotlinMultiplatformAnalysisModeComponent @@ -81,9 +82,11 @@ abstract class AbstractMultiModuleIdeResolveTest : AbstractMultiModuleTest() { file, markDynamicCalls = false, dynamicCallDescriptors = mutableListOf(), - platform = null, // we don't need to attach platform-description string to diagnostic here - withNewInference = false, - languageVersionSettings = resolutionFacade.frontendService(), + configuration = DiagnosticsRenderingConfiguration( + platform = null, // we don't need to attach platform-description string to diagnostic here + withNewInference = false, + languageVersionSettings = resolutionFacade.frontendService(), + ), dataFlowValueFactory = resolutionFacade.frontendService(), moduleDescriptor = moduleDescriptor as ModuleDescriptorImpl ).filter { diagnosticsFilter.value(it.diagnostic) }