Control-Flow Analysis: Reuse pseudo-value information for unused expression analysis

This commit is contained in:
Alexey Sedunov
2014-07-25 14:52:15 +04:00
parent 2a19016d58
commit 9cbcabffa4
55 changed files with 239 additions and 190 deletions
@@ -31,6 +31,7 @@ import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableInitState;
import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState;
import org.jetbrains.jet.lang.cfg.pseudocode.PseudoValue;
import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode;
import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodePackage;
import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodeUtil;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.Instruction;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitor;
@@ -50,6 +51,7 @@ import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
import org.jetbrains.jet.lang.resolve.calls.TailRecursionKind;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
@@ -121,10 +123,10 @@ public class JetFlowInformationProvider {
markUnusedVariables();
markUnusedLiteralsInBlock();
markStatements();
markUnusedExpressions();
markWhenWithoutElse();
}
@@ -666,36 +668,28 @@ public class JetFlowInformationProvider {
}
////////////////////////////////////////////////////////////////////////////////
// "Unused literals" in block
// "Unused expressions" in block
public void markUnusedLiteralsInBlock() {
public void markUnusedExpressions() {
final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
PseudocodeTraverserPackage.traverse(
pseudocode, FORWARD, new FunctionVoid1<Instruction>() {
pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() {
@Override
public void execute(@NotNull Instruction instruction) {
if (!(instruction instanceof ReadValueInstruction || instruction instanceof MagicInstruction)) return;
if (!(instruction instanceof JetElementInstruction)) return;
JetElement element = ((JetElementInstruction) instruction).getElement();
if (!(element instanceof JetFunctionLiteralExpression
|| element instanceof JetConstantExpression
|| element instanceof JetStringTemplateExpression
|| element instanceof JetSimpleNameExpression)) return;
JetElement element = ((JetElementInstruction)instruction).getElement();
if (!(element instanceof JetExpression)) return;
if (!(element instanceof JetStringTemplateExpression || instruction instanceof ReadValueInstruction)) return;
VariableContext ctxt = new VariableContext(instruction, reportedDiagnosticMap);
PsiElement parent = element.getParent();
if (parent instanceof JetBlockExpression) {
if (!JetPsiUtil.isImplicitlyUsed(element)) {
if (element instanceof JetFunctionLiteralExpression) {
report(Errors.UNUSED_FUNCTION_LITERAL.on((JetFunctionLiteralExpression) element), ctxt);
}
else {
report(Errors.UNUSED_EXPRESSION.on(element), ctxt);
}
}
if (BindingContextUtilPackage.isUsedAsStatement((JetExpression) element, trace.getBindingContext())
&& PseudocodePackage.getSideEffectFree(instruction)) {
VariableContext ctxt = new VariableContext(instruction, reportedDiagnosticMap);
report(
element instanceof JetFunctionLiteralExpression
? Errors.UNUSED_FUNCTION_LITERAL.on((JetFunctionLiteralExpression) element)
: Errors.UNUSED_EXPRESSION.on(element),
ctxt
);
}
}
}
@@ -64,4 +64,6 @@ public interface Pseudocode {
@NotNull
List<? extends Instruction> getUsages(@Nullable PseudoValue value);
boolean isSideEffectFree(@NotNull Instruction instruction);
}
@@ -18,13 +18,16 @@ package org.jetbrains.jet.lang.cfg.pseudocode;
import com.google.common.collect.*;
import com.intellij.util.containers.BidirectionalMap;
import jet.runtime.typeinfo.JetValueParameter;
import kotlin.Function0;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.cfg.Label;
import org.jetbrains.jet.lang.cfg.LoopInfo;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.*;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.InstructionWithValue;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.MergeInstruction;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction;
@@ -95,6 +98,7 @@ public class PseudocodeImpl implements Pseudocode {
private final Map<PseudoValue, List<Instruction>> valueUsages = Maps.newHashMap();
private final Map<PseudoValue, Set<PseudoValue>> mergedValues = Maps.newHashMap();
private final Set<Instruction> sideEffectFree = Sets.newHashSet();
private Pseudocode parent = null;
private Set<LocalFunctionDeclarationInstruction> localDeclarations = null;
@@ -239,6 +243,9 @@ public class PseudocodeImpl implements Pseudocode {
addValueUsage(mergedValue, instruction);
}
}
if (PseudocodePackage.calcSideEffectFree(instruction)) {
sideEffectFree.add(instruction);
}
}
/*package*/ void recordLoopInfo(JetExpression expression, LoopInfo blockInfo) {
@@ -283,6 +290,11 @@ public class PseudocodeImpl implements Pseudocode {
return result != null ? result : Collections.<Instruction>emptyList();
}
@Override
public boolean isSideEffectFree(@NotNull Instruction instruction) {
return sideEffectFree.contains(instruction);
}
/*package*/ void bindElementToValue(@NotNull JetElement element, @NotNull PseudoValue value) {
elementsToValues.put(element, value);
}
@@ -131,25 +131,25 @@ public class MagicInstruction(
}
}
public enum class MagicKind {
public enum class MagicKind(val sideEffectFree: Boolean = false) {
// builtin operations
STRING_TEMPLATE
AND
OR
NOT_NULL_ASSERTION
EQUALS_IN_WHEN_CONDITION
IS
CAST
CALLABLE_REFERENCE
STRING_TEMPLATE: MagicKind(true)
AND: MagicKind(true)
OR: MagicKind(true)
NOT_NULL_ASSERTION: MagicKind()
EQUALS_IN_WHEN_CONDITION: MagicKind()
IS: MagicKind()
CAST: MagicKind()
CALLABLE_REFERENCE: MagicKind(true)
// implicit operations
LOOP_RANGE_ITERATION
IMPLICIT_RECEIVER
VALUE_CONSUMER
LOOP_RANGE_ITERATION: MagicKind()
IMPLICIT_RECEIVER: MagicKind()
VALUE_CONSUMER: MagicKind()
// unrecognized operations
UNRESOLVED_CALL
UNSUPPORTED_ELEMENT
UNRECOGNIZED_WRITE_RHS
FAKE_INITIALIZER
UNRESOLVED_CALL: MagicKind()
UNSUPPORTED_ELEMENT: MagicKind()
UNRECOGNIZED_WRITE_RHS: MagicKind()
FAKE_INITIALIZER: MagicKind()
}
// Merges values produced by alternative control-flow paths (such as 'if' branches)
@@ -16,7 +16,6 @@
package org.jetbrains.jet.lang.cfg.pseudocode
import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.*
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.*
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.*
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.*
@@ -31,7 +30,7 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.jet.lang.resolve.OverridingUtil
import org.jetbrains.jet.lang.types.TypeUtils
import org.jetbrains.jet.lang.types.JetType
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction
import org.jetbrains.jet.lang.descriptors.impl.LocalVariableDescriptor
fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: ReceiverValue): TypePredicate? {
val callableDescriptor = resolvedCall.getResultingDescriptor()
@@ -134,4 +133,32 @@ public fun Instruction.getPrimaryDeclarationDescriptorIfAny(bindingContext: Bind
is CallInstruction -> return resolvedCall.getResultingDescriptor()
else -> PseudocodeUtil.extractVariableDescriptorIfAny(this, false, bindingContext)
}
}
public val Instruction.sideEffectFree: Boolean
get() = owner.isSideEffectFree(this)
private fun Instruction.calcSideEffectFree(): Boolean {
if (this !is InstructionWithValue) return false
if (!inputValues.all { it.createdAt?.sideEffectFree ?: false }) return false
return when (this) {
is ReadValueInstruction -> target.let {
when (it) {
is AccessTarget.Call -> when (it.resolvedCall.getResultingDescriptor()) {
is LocalVariableDescriptor, is ValueParameterDescriptor, is ReceiverParameterDescriptor -> true
else -> false
}
else -> when (element) {
is JetConstantExpression, is JetFunctionLiteralExpression, is JetStringTemplateExpression -> true
else -> false
}
}
}
is MagicInstruction -> kind.sideEffectFree
else -> false
}
}
@@ -282,29 +282,6 @@ public class JetPsiUtil {
return null;
}
public static boolean isImplicitlyUsed(@NotNull JetElement element) {
PsiElement parent = element.getParent();
if (!(parent instanceof JetBlockExpression)) return true;
JetBlockExpression block = (JetBlockExpression) parent;
List<JetElement> statements = block.getStatements();
if (statements.get(statements.size() - 1) == element) {
JetExpression expression = getDirectParentOfTypeForBlock(block, JetIfExpression.class);
if (expression == null) {
expression = getDirectParentOfTypeForBlock(block, JetWhenExpression.class);
}
if (expression == null) {
expression = getDirectParentOfTypeForBlock(block, JetFunctionLiteral.class);
}
if (expression == null) {
expression = getDirectParentOfTypeForBlock(block, JetTryExpression.class);
}
if (expression != null) {
return isImplicitlyUsed(expression);
}
}
return false;
}
public static void deleteClass(@NotNull JetClassOrObject clazz) {
CheckUtil.checkWritable(clazz);
JetFile file = clazz.getContainingJetFile();
@@ -265,7 +265,6 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
else {
facade.getTypeInfo(body, context.replaceScope(context.scope));
}
context.trace.report(UNUSED_FUNCTION_LITERAL.on(function));
}
else if (body != null) {
WritableScope writableScope = newWritableScopeImpl(context, "do..while body scope");