[Spec tests] Add DEBUG_INFO_AS_CALL diagnostic tag for callable references
This commit is contained in:
committed by
Victor Petukhov
parent
008a0df28f
commit
f7ff60cb77
+10
-1
@@ -34,7 +34,7 @@ class DebugInfoDiagnosticFactory1 : DiagnosticFactory1<PsiElement, String>,
|
|||||||
languageVersionSettings: LanguageVersionSettings?,
|
languageVersionSettings: LanguageVersionSettings?,
|
||||||
moduleDescriptor: ModuleDescriptorImpl?
|
moduleDescriptor: ModuleDescriptorImpl?
|
||||||
) = when (name) {
|
) = when (name) {
|
||||||
"EXPRESSION_TYPE" -> {
|
EXPRESSION_TYPE.name -> {
|
||||||
val (type, dataFlowTypes) = CheckerTestUtil.getTypeInfo(
|
val (type, dataFlowTypes) = CheckerTestUtil.getTypeInfo(
|
||||||
expression,
|
expression,
|
||||||
bindingContext,
|
bindingContext,
|
||||||
@@ -45,6 +45,10 @@ class DebugInfoDiagnosticFactory1 : DiagnosticFactory1<PsiElement, String>,
|
|||||||
|
|
||||||
this.on(expression, Renderers.renderExpressionType(type, dataFlowTypes))
|
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.")
|
else -> throw NotImplementedError("Creation diagnostic '$name' isn't supported.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +71,11 @@ class DebugInfoDiagnosticFactory1 : DiagnosticFactory1<PsiElement, String>,
|
|||||||
Severity.INFO,
|
Severity.INFO,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
val CALL = create(
|
||||||
|
"CALL",
|
||||||
|
Severity.INFO,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
fun create(name: String, severity: Severity): DebugInfoDiagnosticFactory1 {
|
fun create(name: String, severity: Severity): DebugInfoDiagnosticFactory1 {
|
||||||
return DebugInfoDiagnosticFactory1(name, severity)
|
return DebugInfoDiagnosticFactory1(name, severity)
|
||||||
|
|||||||
@@ -19,21 +19,31 @@ import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFa
|
|||||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
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.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.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.isCommon
|
||||||
import org.jetbrains.kotlin.platform.oldFashionedDescription
|
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.KotlinType
|
||||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
|
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
|
||||||
|
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
@@ -176,27 +186,115 @@ object CheckerTestUtil {
|
|||||||
BindingContext.IMPLICIT_EXHAUSTIVE_WHEN to DebugInfoDiagnosticFactory0.IMPLICIT_EXHAUSTIVE
|
BindingContext.IMPLICIT_EXHAUSTIVE_WHEN to DebugInfoDiagnosticFactory0.IMPLICIT_EXHAUSTIVE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val kotlinSpecificInfo = KotlinSpecificInfo(
|
||||||
|
platform,
|
||||||
|
withNewInference,
|
||||||
|
languageVersionSettings,
|
||||||
|
)
|
||||||
|
/** for expressions*/
|
||||||
for ((context, factory) in factoryList) {
|
for ((context, factory) in factoryList) {
|
||||||
for ((expression, _) in bindingContext.getSliceContents(context)) {
|
renderDiagnosticsForExpressions(
|
||||||
val needRender = !factory.withExplicitDefinitionOnly
|
context,
|
||||||
|| diagnosedRanges?.get(expression.startOffset..expression.endOffset)?.contains(factory.name) == true
|
factory,
|
||||||
|
root,
|
||||||
if (PsiTreeUtil.isAncestor(root, expression, false) && needRender) {
|
bindingContext,
|
||||||
val diagnostic = factory.createDiagnostic(
|
kotlinSpecificInfo,
|
||||||
expression,
|
dataFlowValueFactory,
|
||||||
bindingContext,
|
moduleDescriptor,
|
||||||
dataFlowValueFactory,
|
diagnosedRanges,
|
||||||
languageVersionSettings,
|
debugAnnotations
|
||||||
moduleDescriptor
|
)
|
||||||
)
|
|
||||||
debugAnnotations.add(ActualDiagnostic(diagnostic, platform, withNewInference))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** for calls*/
|
||||||
|
renderDiagnosticsForCalls(
|
||||||
|
BindingContext.RESOLVED_CALL,
|
||||||
|
DebugInfoDiagnosticFactory1.CALL,
|
||||||
|
root,
|
||||||
|
bindingContext,
|
||||||
|
kotlinSpecificInfo,
|
||||||
|
dataFlowValueFactory,
|
||||||
|
moduleDescriptor,
|
||||||
|
diagnosedRanges,
|
||||||
|
debugAnnotations
|
||||||
|
)
|
||||||
|
|
||||||
return debugAnnotations
|
return debugAnnotations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class KotlinSpecificInfo(
|
||||||
|
val platform: String?,
|
||||||
|
val withNewInference: Boolean,
|
||||||
|
val languageVersionSettings: LanguageVersionSettings?
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun renderDiagnosticsForCalls(
|
||||||
|
context: WritableSlice<Call, ResolvedCall<*>>, callFactory: DebugInfoDiagnosticFactory1,
|
||||||
|
root: PsiElement, bindingContext: BindingContext, kotlinSpecificInfo: KotlinSpecificInfo,
|
||||||
|
dataFlowValueFactory: DataFlowValueFactory?,
|
||||||
|
moduleDescriptor: ModuleDescriptorImpl?,
|
||||||
|
diagnosedRanges: Map<IntRange, MutableSet<String>>?,
|
||||||
|
|
||||||
|
debugAnnotations: MutableList<ActualDiagnostic>
|
||||||
|
) {
|
||||||
|
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<out KtExpression, out Any>, factory: DebugInfoDiagnosticFactory,
|
||||||
|
root: PsiElement,
|
||||||
|
bindingContext: BindingContext,
|
||||||
|
kotlinSpecificInfo: KotlinSpecificInfo,
|
||||||
|
|
||||||
|
dataFlowValueFactory: DataFlowValueFactory?,
|
||||||
|
moduleDescriptor: ModuleDescriptorImpl?,
|
||||||
|
diagnosedRanges: Map<IntRange, MutableSet<String>>?,
|
||||||
|
|
||||||
|
debugAnnotations: MutableList<ActualDiagnostic>
|
||||||
|
) {
|
||||||
|
|
||||||
|
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<IntRange, MutableSet<String>>?,
|
||||||
|
|
||||||
|
debugAnnotations: MutableList<ActualDiagnostic>
|
||||||
|
) {
|
||||||
|
(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(
|
fun diagnosticsDiff(
|
||||||
expected: List<DiagnosedRange>,
|
expected: List<DiagnosedRange>,
|
||||||
@@ -627,4 +725,51 @@ object CheckerTestUtil {
|
|||||||
}
|
}
|
||||||
return Pair(result, null)
|
return Pair(result, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getCallDebugInfo(
|
||||||
|
expression: PsiElement,
|
||||||
|
bindingContext: BindingContext,
|
||||||
|
): Pair<FqNameUnsafe?, String?> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.newTable
|
||||||
import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newText
|
import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newText
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.platform.isCommon
|
import org.jetbrains.kotlin.platform.isCommon
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -738,6 +739,13 @@ object Renderers {
|
|||||||
|
|
||||||
return typesAsString.sorted().joinToString(separator = " & ")
|
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)
|
fun DescriptorRenderer.asRenderer() = SmartDescriptorRenderer(this)
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ public interface BindingContext {
|
|||||||
WritableSlice<KtTypeReference, KotlinType> TYPE = Slices.createSimpleSlice();
|
WritableSlice<KtTypeReference, KotlinType> TYPE = Slices.createSimpleSlice();
|
||||||
WritableSlice<KtTypeReference, KotlinType> ABBREVIATED_TYPE = Slices.createSimpleSlice();
|
WritableSlice<KtTypeReference, KotlinType> ABBREVIATED_TYPE = Slices.createSimpleSlice();
|
||||||
WritableSlice<KtExpression, KotlinTypeInfo> EXPRESSION_TYPE_INFO = new BasicWritableSlice<>(DO_NOTHING);
|
WritableSlice<KtExpression, KotlinTypeInfo> EXPRESSION_TYPE_INFO = new BasicWritableSlice<>(DO_NOTHING);
|
||||||
|
WritableSlice<KtExpression, KotlinTypeInfo> FQ_NAME = new BasicWritableSlice<>(DO_NOTHING);
|
||||||
WritableSlice<KtExpression, DataFlowInfo> DATA_FLOW_INFO_BEFORE = new BasicWritableSlice<>(DO_NOTHING);
|
WritableSlice<KtExpression, DataFlowInfo> DATA_FLOW_INFO_BEFORE = new BasicWritableSlice<>(DO_NOTHING);
|
||||||
WritableSlice<KtExpression, KotlinType> EXPECTED_EXPRESSION_TYPE = new BasicWritableSlice<>(DO_NOTHING);
|
WritableSlice<KtExpression, KotlinType> EXPECTED_EXPRESSION_TYPE = new BasicWritableSlice<>(DO_NOTHING);
|
||||||
WritableSlice<KtElement, Computation> EXPRESSION_EFFECTS = Slices.createSimpleSlice();
|
WritableSlice<KtElement, Computation> EXPRESSION_EFFECTS = Slices.createSimpleSlice();
|
||||||
|
|||||||
Reference in New Issue
Block a user