Control-Flow Analysis: Reuse pseudo-value information for unused expression analysis
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
+16
-16
@@ -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();
|
||||
|
||||
-1
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user