[Spec tests] Add DEBUG_INFO_AS_CALL diagnostic tag for callable references

This commit is contained in:
anastasiia.spaseeva
2020-03-19 15:13:32 +03:00
committed by Victor Petukhov
parent 008a0df28f
commit f7ff60cb77
4 changed files with 188 additions and 25 deletions
@@ -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();