Control-Flow Analysis: Compute STATEMENT slice using control-flow information

This commit is contained in:
Alexey Sedunov
2014-07-11 19:30:25 +04:00
parent ffd4af872c
commit 00dd1dbb0b
19 changed files with 99 additions and 70 deletions
@@ -42,6 +42,7 @@ public class ConvertToForEachLoopIntention : JetSelfTargetingIntention<JetExpres
else -> null
}
}
else -> null
}
}
is JetBinaryExpression -> element.getRight()
@@ -28,7 +28,6 @@ import org.jetbrains.jet.plugin.refactoring.introduce.introduceVariable.KotlinIn
import org.jetbrains.jet.lang.psi.JetSafeQualifiedExpression
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import com.intellij.psi.PsiElement
import org.jetbrains.jet.plugin.refactoring.inline.KotlinInlineValHandler
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
@@ -43,6 +42,7 @@ import org.jetbrains.jet.lang.psi.JetPostfixExpression
import org.jetbrains.jet.lang.psi.JetCallExpression
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.lang.psi.JetElement
import org.jetbrains.jet.lang.resolve.bindingContextUtil.isUsedAsStatement
val NULL_PTR_EXCEPTION = "NullPointerException"
val NULL_PTR_EXCEPTION_FQ = "java.lang.NullPointerException"
@@ -72,17 +72,7 @@ fun JetExpression.extractExpressionIfSingle(): JetExpression? {
return innerExpression
}
fun JetExpression.isStatement(): Boolean {
val context = AnalyzerFacadeWithCache.getContextForElement(this)
val expectedType = context.get(BindingContext.EXPECTED_EXPRESSION_TYPE, this);
val isUnit = expectedType != null && KotlinBuiltIns.getInstance().isUnit(expectedType);
// Some "statements" are actually expressions returned from lambdas, their expected types are non-null
val isStatement = context.get(BindingContext.STATEMENT, this) == true && expectedType == null;
return isStatement || isUnit
}
fun JetExpression.isStatement(): Boolean = isUsedAsStatement(AnalyzerFacadeWithCache.getContextForElement(this))
fun JetBinaryExpression.getNonNullExpression(): JetExpression? = when {
this.getLeft()?.isNullExpression() == false ->
@@ -35,9 +35,9 @@ import com.intellij.codeInsight.template.TemplateBuilderImpl
import com.intellij.psi.PsiDocumentManager
import org.apache.commons.lang.StringEscapeUtils.escapeJava
import org.jetbrains.jet.plugin.intentions.branchedTransformations.convertToIfNullExpression
import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStatement
import org.jetbrains.jet.plugin.intentions.branchedTransformations.NULL_PTR_EXCEPTION
import org.jetbrains.jet.plugin.intentions.branchedTransformations.KOTLIN_NULL_PTR_EXCEPTION
import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStatement
public class DoubleBangToIfThenIntention : JetSelfTargetingIntention<JetPostfixExpression>("double.bang.to.if.then", javaClass()) {
@@ -32,9 +32,8 @@ import org.jetbrains.jet.plugin.intentions.branchedTransformations.extractExpres
import org.jetbrains.jet.lang.psi.JetThrowExpression
import org.jetbrains.jet.plugin.JetBundle
import org.jetbrains.jet.plugin.intentions.branchedTransformations.isNullExpressionOrEmptyBlock
import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStatement
import org.jetbrains.jet.plugin.intentions.branchedTransformations.throwsNullPointerExceptionWithNoArguments
import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStatement
public class IfThenToDoubleBangIntention : JetSelfTargetingIntention<JetIfExpression>("if.then.to.double.bang", javaClass()) {
@@ -25,8 +25,8 @@ import org.jetbrains.jet.lang.psi.JetBinaryExpression
import org.jetbrains.jet.plugin.intentions.branchedTransformations.convertToIfNotNullExpression
import org.jetbrains.jet.plugin.intentions.branchedTransformations.introduceValueForCondition
import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression
import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStatement
import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStableVariable
import org.jetbrains.jet.plugin.intentions.branchedTransformations.isStatement
public class SafeAccessToIfThenIntention : JetSelfTargetingIntention<JetSafeQualifiedExpression>("safe.access.to.if.then", javaClass()) {
override fun isApplicableTo(element: JetSafeQualifiedExpression): Boolean = true
@@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.analyzer.AnalyzerPackage;
import org.jetbrains.jet.di.InjectorForBodyResolve;
import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
import org.jetbrains.jet.lang.psi.*;
@@ -173,6 +174,8 @@ public class ResolveElementCache {
assert false : "Invalid type of the topmost parent";
}
new JetFlowInformationProvider(resolveElement, trace).checkDeclaration();
return trace.getBindingContext();
}
@@ -315,6 +318,10 @@ public class ResolveElementCache {
}
bodyResolver.resolvePropertyAccessors(bodyResolveContext, jetProperty, descriptor);
for (JetPropertyAccessor accessor : jetProperty.getAccessors()) {
new JetFlowInformationProvider(accessor, trace).checkDeclaration();
}
}
private static void functionAdditionalResolve(
@@ -64,6 +64,7 @@ import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder
import org.jetbrains.jet.lang.resolve.bindingContextUtil.getTargetFunctionDescriptor
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.jet.lang.resolve.OverridingUtil
import org.jetbrains.jet.lang.resolve.bindingContextUtil.isUsedAsStatement
import org.jetbrains.jet.lang.psi.psiUtil.isAncestor
import org.jetbrains.jet.plugin.intentions.declarations.DeclarationUtils
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
@@ -93,7 +94,6 @@ private fun List<Instruction>.getExitPoints(): List<Instruction> =
filter { localInstruction -> localInstruction.nextInstructions.any { it !in this } }
private fun List<Instruction>.getResultType(
pseudocode: Pseudocode,
bindingContext: BindingContext,
options: ExtractionOptions): JetType {
fun instructionToType(instruction: Instruction): JetType? {
@@ -106,7 +106,7 @@ private fun List<Instruction>.getResultType(
}
if (expression == null) return null
if (options.inferUnitTypeForUnusedValues && expression.isStatement(pseudocode)) return null
if (options.inferUnitTypeForUnusedValues && expression.isUsedAsStatement(bindingContext)) return null
return bindingContext[BindingContext.EXPRESSION_TYPE, expression]
}
@@ -206,8 +206,8 @@ private fun ExtractionData.analyzeControlFlow(
val nonLocallyUsedDeclarations = getLocalDeclarationsWithNonLocalUsages(pseudocode, localInstructions, bindingContext)
val (declarationsToCopy, declarationsToReport) = nonLocallyUsedDeclarations.partition { it is JetProperty && it.isLocal() }
val typeOfDefaultFlow = defaultExits.getResultType(pseudocode, bindingContext, options)
val returnValueType = valuedReturnExits.getResultType(pseudocode, bindingContext, options)
val typeOfDefaultFlow = defaultExits.getResultType(bindingContext, options)
val returnValueType = valuedReturnExits.getResultType(bindingContext, options)
val defaultReturnType = if (returnValueType.isMeaningful()) returnValueType else typeOfDefaultFlow
if (defaultReturnType.isError()) return Pair(DefaultControlFlow(DEFAULT_RETURN_TYPE, declarationsToCopy), ErrorMessage.ERROR_TYPES)