Distinguish PARTIAL_WITH_CFA resolve mode from just PARTIAL

So #KT-23182 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-03-27 10:43:14 +02:00
parent 5d57341ee4
commit 9170aa0674
20 changed files with 39 additions and 30 deletions
@@ -18,11 +18,17 @@ package org.jetbrains.kotlin.resolve.lazy
import org.jetbrains.kotlin.resolve.BindingTraceFilter
enum class BodyResolveMode(val bindingTraceFilter: BindingTraceFilter) {
FULL(BindingTraceFilter.ACCEPT_ALL),
PARTIAL_FOR_COMPLETION(BindingTraceFilter.NO_DIAGNOSTICS),
PARTIAL_WITH_DIAGNOSTICS(BindingTraceFilter.ACCEPT_ALL),
PARTIAL(BindingTraceFilter.NO_DIAGNOSTICS)
enum class BodyResolveMode(val bindingTraceFilter: BindingTraceFilter, val doControlFlowAnalysis: Boolean) {
// All body statements are analyzed, diagnostics included
FULL(BindingTraceFilter.ACCEPT_ALL, doControlFlowAnalysis = true),
// Analyzes only dependent statements, including all declaration statements (difference from PARTIAL_WITH_CFA)
PARTIAL_FOR_COMPLETION(BindingTraceFilter.NO_DIAGNOSTICS, doControlFlowAnalysis = true),
// Analyzes only dependent statements, diagnostics included
PARTIAL_WITH_DIAGNOSTICS(BindingTraceFilter.ACCEPT_ALL, doControlFlowAnalysis = true),
// Analyzes only dependent statements, performs control flow analysis (mostly needed for isUsedAsExpression / AsStatement)
PARTIAL_WITH_CFA(BindingTraceFilter.NO_DIAGNOSTICS, doControlFlowAnalysis = true),
// Analyzes only dependent statements, including only used declaration statements, does not perform control flow analysis
PARTIAL(BindingTraceFilter.NO_DIAGNOSTICS, doControlFlowAnalysis = false)
;
@@ -409,12 +409,15 @@ class ResolveElementCache(
}
}
val controlFlowTrace =
DelegatingBindingTrace(trace.bindingContext, "Element control flow resolve", resolveElement, allowSliceRewrite = true)
ControlFlowInformationProvider(
resolveElement, controlFlowTrace, resolveElement.languageVersionSettings, resolveSession.platformDiagnosticSuppressor
).checkDeclaration()
controlFlowTrace.addOwnDataTo(trace, null, false)
if (bodyResolveMode.doControlFlowAnalysis) {
val controlFlowTrace = DelegatingBindingTrace(
trace.bindingContext, "Element control flow resolve", resolveElement, allowSliceRewrite = true
)
ControlFlowInformationProvider(
resolveElement, controlFlowTrace, resolveElement.languageVersionSettings, resolveSession.platformDiagnosticSuppressor
).checkDeclaration()
controlFlowTrace.addOwnDataTo(trace, null, false)
}
return Pair(trace.bindingContext, statementFilterUsed)
}
@@ -173,7 +173,7 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
// step 2: analyze collected elements with resolve and decide which can be shortened now and which need descriptors to be imported before shortening
val allElementsToAnalyze = visitors.flatMap { it.getElementsToAnalyze().map { it.element } }
val bindingContext = file.getResolutionFacade().analyze(allElementsToAnalyze, BodyResolveMode.PARTIAL)
val bindingContext = file.getResolutionFacade().analyze(allElementsToAnalyze, BodyResolveMode.PARTIAL_WITH_CFA)
processors.forEach { it.analyzeCollectedElements(bindingContext) }
// step 3: shorten elements that can be shortened right now
@@ -29,7 +29,7 @@ class CallableUsageReplacementStrategy(
private val inlineSetter: Boolean = false
) : UsageReplacementStrategy {
override fun createReplacer(usage: KtSimpleNameExpression): (() -> KtElement?)? {
val bindingContext = usage.analyze(BodyResolveMode.PARTIAL)
val bindingContext = usage.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
val resolvedCall = usage.getResolvedCall(bindingContext) ?: return null
if (!resolvedCall.status.isSuccess) return null
@@ -84,7 +84,7 @@ class KotlinExpressionTypeProvider : ExpressionTypeProvider<KtExpression>() {
private fun KtExpression.shouldShowStatementType(): Boolean {
if (parent !is KtBlockExpression) return true
if (parent.children.lastOrNull() == this) {
return analyze(BodyResolveMode.PARTIAL)[BindingContext.USED_AS_EXPRESSION, this] ?: false
return analyze(BodyResolveMode.PARTIAL_WITH_CFA)[BindingContext.USED_AS_EXPRESSION, this] ?: false
}
return false
}
@@ -49,12 +49,12 @@ public class KotlinSurrounderUtils {
}
public static boolean isUsedAsStatement(@NotNull KtExpression expression) {
BindingContext context = ResolutionUtils.analyze(expression, BodyResolveMode.PARTIAL);
BindingContext context = ResolutionUtils.analyze(expression, BodyResolveMode.PARTIAL_WITH_CFA);
return BindingContextUtilsKt.isUsedAsStatement(expression, context);
}
public static boolean isUsedAsExpression(@NotNull KtExpression expression) {
BindingContext context = ResolutionUtils.analyze(expression, BodyResolveMode.PARTIAL);
BindingContext context = ResolutionUtils.analyze(expression, BodyResolveMode.PARTIAL_WITH_CFA);
return BindingContextUtilsKt.isUsedAsExpression(expression, context);
}
}
@@ -29,7 +29,7 @@ class ConstantConditionIfInspection : AbstractKotlinInspection() {
return ifExpressionVisitor(fun(expression) {
val condition = expression.condition ?: return
val context = condition.analyze(BodyResolveMode.PARTIAL)
val context = condition.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
val constantValue = condition.constantBooleanValue(context) ?: return
val fixes = mutableListOf<LocalQuickFix>()
@@ -40,7 +40,7 @@ class ReplaceWithOperatorAssignmentInspection : AbstractApplicabilityBasedInspec
val right = element.right as? KtBinaryExpression ?: return false
if (right.left == null || right.right == null) return false
val bindingContext = right.analyze(BodyResolveMode.PARTIAL)
val bindingContext = right.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
if (!checkExpressionRepeat(left, right, bindingContext)) return false
// now check that the resulting operator assignment will be resolved
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.resolve.source.getPsi
class UnusedLambdaExpressionBodyInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return callExpressionVisitor(fun(expression) {
val context = expression.analyze(BodyResolveMode.PARTIAL)
val context = expression.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
if (expression.used(context)) {
return
}
@@ -55,7 +55,7 @@ class ReplaceGetOrSetInspection : AbstractApplicabilityBasedInspection<KtDotQual
override fun isApplicable(element: KtDotQualifiedExpression): Boolean {
val callExpression = element.callExpression ?: return false
val bindingContext = callExpression.analyze(BodyResolveMode.PARTIAL)
val bindingContext = callExpression.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
val resolvedCall = callExpression.getResolvedCall(bindingContext) ?: return false
if (!resolvedCall.isReallySuccess()) return false
@@ -39,7 +39,7 @@ class AddForLoopIndicesIntention : SelfTargetingRangeIntention<KtForExpression>(
if (element.loopParameter == null) return null
val loopRange = element.loopRange ?: return null
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
val bindingContext = element.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
val resolvedCall = loopRange.getResolvedCall(bindingContext)
if (resolvedCall?.resultingDescriptor?.fqNameUnsafe?.asString() in WITH_INDEX_FQ_NAMES) return null // already withIndex() call
@@ -50,7 +50,7 @@ class ConvertAssertToIfWithThrowIntention : SelfTargetingIntention<KtCallExpress
val args = element.valueArguments
val conditionText = args[0]?.getArgumentExpression()?.text ?: return
val functionLiteralArgument = element.lambdaArguments.singleOrNull()
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
val bindingContext = element.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
val psiFactory = KtPsiFactory(element)
val messageFunctionExpr = when {
@@ -48,7 +48,7 @@ class RemoveExplicitSuperQualifierIntention : SelfTargetingRangeIntention<KtSupe
val qualifiedExpression = element.getQualifiedExpressionForReceiver() ?: return null
val selector = qualifiedExpression.selectorExpression ?: return null
val bindingContext = selector.analyze(BodyResolveMode.PARTIAL)
val bindingContext = selector.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
if (selector.getResolvedCall(bindingContext) == null) return null
val newQualifiedExpression = KtPsiFactory(element).createExpressionByPattern(
@@ -55,7 +55,7 @@ class RemoveExplicitTypeArgumentsIntention : SelfTargetingOffsetIndependentInten
if (callExpression.typeArguments.isEmpty()) return false
val resolutionFacade = callExpression.getResolutionFacade()
val bindingContext = resolutionFacade.analyze(callExpression, BodyResolveMode.PARTIAL)
val bindingContext = resolutionFacade.analyze(callExpression, BodyResolveMode.PARTIAL_WITH_CFA)
val originalCall = callExpression.getResolvedCall(bindingContext) ?: return false
val (contextExpression, expectedType) = findContextToAnalyze(callExpression, bindingContext)
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class IfThenToElvisInspection : IntentionBasedInspection<KtIfExpression>(
IfThenToElvisIntention::class,
{ it -> it.isUsedAsExpression(it.analyze(BodyResolveMode.PARTIAL)) }
{ it -> it.isUsedAsExpression(it.analyze(BodyResolveMode.PARTIAL_WITH_CFA)) }
) {
override fun inspectionTarget(element: KtIfExpression) = element.ifKeyword
@@ -390,7 +390,7 @@ private fun isEmbeddedBreakOrContinue(expression: KtExpressionWithLabel): Boolea
is KtContainerNode -> {
val containerExpression = parent.parent as KtExpression
containerExpression.isUsedAsExpression(containerExpression.analyze(BodyResolveMode.PARTIAL))
containerExpression.isUsedAsExpression(containerExpression.analyze(BodyResolveMode.PARTIAL_WITH_CFA))
}
else -> true
@@ -96,7 +96,7 @@ class RenameUnresolvedReferenceFix(element: KtNameReferenceExpression): KotlinQu
}
val resolutionFacade = element.getResolutionFacade()
val context = resolutionFacade.analyze(element, BodyResolveMode.PARTIAL)
val context = resolutionFacade.analyze(element, BodyResolveMode.PARTIAL_WITH_CFA)
val moduleDescriptor = resolutionFacade.moduleDescriptor
val variantsHelper = ReferenceVariantsHelper(context, resolutionFacade, moduleDescriptor, {
it !is DeclarationDescriptorWithVisibility || it.isVisible(element, null, context, resolutionFacade)
@@ -61,7 +61,7 @@ class SurroundWithNullCheckFix(
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val element = diagnostic.psiElement
val expressionParent = element.getParentOfType<KtExpression>(strict = element is KtOperationReferenceExpression) ?: return null
val context = expressionParent.analyze(BodyResolveMode.PARTIAL)
val context = expressionParent.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
val parent = element.parent
val nullableExpression =
@@ -42,7 +42,7 @@ object CreateFunctionFromCallableReferenceActionFactory : CreateCallableMemberFr
override fun extractFixData(element: KtCallableReferenceExpression, diagnostic: Diagnostic): List<CallableInfo> {
val name = element.callableReference.getReferencedName()
val resolutionFacade = element.getResolutionFacade()
val context = resolutionFacade.analyze(element, BodyResolveMode.PARTIAL)
val context = resolutionFacade.analyze(element, BodyResolveMode.PARTIAL_WITH_CFA)
return element
.guessTypes(context, resolutionFacade.moduleDescriptor)
.ifEmpty { element.guessTypes(context, resolutionFacade.moduleDescriptor, allowErrorTypes = true) } // approximate with Any
@@ -80,7 +80,7 @@ class AddTargetVersionCheckQuickFix(val api: Int) : AndroidLintQuickFix {
}
private fun getSurrounder(element: KtElement, todoText: String?): KotlinIfSurrounder {
val used = element.analyze(BodyResolveMode.PARTIAL)[BindingContext.USED_AS_EXPRESSION, element] ?: false
val used = element.analyze(BodyResolveMode.PARTIAL_WITH_CFA)[BindingContext.USED_AS_EXPRESSION, element] ?: false
return if (used) {
object : KotlinIfSurrounder() {
override fun getCodeTemplate(): String = "if (a) { \n} else {\nTODO(${todoText ?: ""})\n}"