mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
DfaVariableSource introduced; now DfaVariableValue does not require a backing PSI element
DfaConstValue preserves element PsiType Other polishing
This commit is contained in:
@@ -20,6 +20,7 @@ import com.intellij.codeInspection.dataFlow.instructions.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaUnknownValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -41,7 +42,7 @@ import java.util.function.Consumer;
|
||||
public class CFGBuilder {
|
||||
private final ControlFlowAnalyzer myAnalyzer;
|
||||
private final Deque<JumpInstruction> myBranches = new ArrayDeque<>();
|
||||
private final Map<PsiExpression, PsiVariable> myMethodRefQualifiers = new HashMap<>();
|
||||
private final Map<PsiExpression, DfaVariableValue> myMethodRefQualifiers = new HashMap<>();
|
||||
|
||||
CFGBuilder(ControlFlowAnalyzer analyzer) {
|
||||
myAnalyzer = analyzer;
|
||||
@@ -91,7 +92,7 @@ public class CFGBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate instructions to push given variable on stack for subsequent write.
|
||||
* Generate instructions to push given variable value on stack for subsequent write.
|
||||
* <p>
|
||||
* Stack before: ...
|
||||
* <p>
|
||||
@@ -100,9 +101,8 @@ public class CFGBuilder {
|
||||
* @param variable to push
|
||||
* @return this builder
|
||||
*/
|
||||
public CFGBuilder pushVariable(PsiVariable variable) {
|
||||
myAnalyzer.addInstruction(
|
||||
new PushInstruction(getFactory().getVarFactory().createVariableValue(variable, false), null, true));
|
||||
public CFGBuilder pushForWrite(DfaVariableValue variable) {
|
||||
myAnalyzer.addInstruction(new PushInstruction(variable, null, true));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -394,7 +394,7 @@ public class CFGBuilder {
|
||||
|
||||
/**
|
||||
* Generate instructions to assign top stack value to the second stack value
|
||||
* (usually pushed via {@link #pushVariable(PsiVariable)}).
|
||||
* (usually pushed via {@link #pushForWrite(DfaVariableValue)}).
|
||||
* <p>
|
||||
* Stack before: ... variable_for_write value
|
||||
* <p>
|
||||
@@ -417,7 +417,20 @@ public class CFGBuilder {
|
||||
* @return this builder
|
||||
*/
|
||||
public CFGBuilder assignTo(PsiVariable var) {
|
||||
return pushVariable(var).swap().assign();
|
||||
return pushForWrite(getFactory().getVarFactory().createVariableValue(var)).swap().assign();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate instructions to assign top stack value to the specified variable
|
||||
* <p>
|
||||
* Stack before: ... value
|
||||
* <p>
|
||||
* Stack after: ... variable
|
||||
*
|
||||
* @return this builder
|
||||
*/
|
||||
public CFGBuilder assignTo(DfaVariableValue var) {
|
||||
return pushForWrite(var).swap().assign();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -445,8 +458,8 @@ public class CFGBuilder {
|
||||
PsiMethodReferenceExpression methodRef = (PsiMethodReferenceExpression)stripped;
|
||||
PsiExpression qualifier = methodRef.getQualifierExpression();
|
||||
if (qualifier != null && !PsiMethodReferenceUtil.isStaticallyReferenced(methodRef)) {
|
||||
PsiVariable qualifierBinding = createTempVariable(qualifier.getType());
|
||||
pushVariable(qualifierBinding)
|
||||
DfaVariableValue qualifierBinding = createTempVariable(qualifier.getType());
|
||||
pushForWrite(qualifierBinding)
|
||||
.pushExpression(qualifier)
|
||||
.checkNotNull(qualifier, NullabilityProblemKind.fieldAccessNPE)
|
||||
.assign()
|
||||
@@ -509,10 +522,8 @@ public class CFGBuilder {
|
||||
}
|
||||
if (argCount == expectedArgCount) {
|
||||
if (pushQualifier) {
|
||||
PsiVariable qualifierVar = myMethodRefQualifiers.remove(methodRef);
|
||||
DfaValue qualifierValue = qualifierVar == null ? DfaUnknownValue.getInstance() :
|
||||
getFactory().getVarFactory().createVariableValue(qualifierVar, false);
|
||||
push(qualifierValue);
|
||||
DfaValue qualifierValue = myMethodRefQualifiers.remove(methodRef);
|
||||
push(qualifierValue == null ? DfaUnknownValue.getInstance() : qualifierValue);
|
||||
moveTopValue(argCount);
|
||||
}
|
||||
myAnalyzer.addBareCall(null, methodRef);
|
||||
@@ -600,9 +611,9 @@ public class CFGBuilder {
|
||||
checkNotNull(expression, NullabilityProblemKind.nullableFunctionReturn);
|
||||
}
|
||||
} else if(body instanceof PsiCodeBlock) {
|
||||
PsiVariable variable = createTempVariable(LambdaUtil.getFunctionalInterfaceReturnType(lambda));
|
||||
DfaVariableValue variable = createTempVariable(LambdaUtil.getFunctionalInterfaceReturnType(lambda));
|
||||
myAnalyzer.inlineBlock((PsiCodeBlock)body, resultNullness, variable);
|
||||
push(getFactory().getVarFactory().createVariableValue(variable, false));
|
||||
push(variable);
|
||||
} else {
|
||||
pushUnknown();
|
||||
}
|
||||
@@ -610,13 +621,13 @@ public class CFGBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temporary {@link PsiVariable} (not declared in the original code) to be used within this control flow.
|
||||
* Create a synthetic variable (not declared in the original code) to be used within this control flow.
|
||||
*
|
||||
* @param type a type of variable to create
|
||||
* @return newly created variable
|
||||
*/
|
||||
@NotNull
|
||||
public PsiVariable createTempVariable(@Nullable PsiType type) {
|
||||
public DfaVariableValue createTempVariable(@Nullable PsiType type) {
|
||||
return myAnalyzer.createTempVariable(type);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ class ContractChecker extends DataFlowRunner {
|
||||
DfaConstValue comparisonValue = constraint.getComparisonValue(factory);
|
||||
if (comparisonValue != null) {
|
||||
boolean negated = constraint.shouldUseNonEqComparison();
|
||||
DfaVariableValue dfaParam = factory.getVarFactory().createVariableValue(parameters[i], false);
|
||||
DfaVariableValue dfaParam = factory.getVarFactory().createVariableValue(parameters[i]);
|
||||
initialState.applyCondition(factory.createCondition(dfaParam, RelationType.equivalence(!negated), comparisonValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.util.Function;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
@@ -52,7 +53,7 @@ public abstract class ContractValue {
|
||||
return new Spec(this, field);
|
||||
}
|
||||
|
||||
public static ContractValue constant(Object value, PsiType type) {
|
||||
public static ContractValue constant(Object value, @NotNull PsiType type) {
|
||||
return new IndependentValue(factory -> factory.getConstFactory().createFromValue(value, type, null), String.valueOf(value));
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ public class ControlFlow {
|
||||
|
||||
public void removeVariable(@Nullable PsiVariable variable) {
|
||||
if (variable == null) return;
|
||||
addInstruction(new FlushVariableInstruction(myFactory.getVarFactory().createVariableValue(variable, false)));
|
||||
addInstruction(new FlushVariableInstruction(myFactory.getVarFactory().createVariableValue(variable)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,7 +111,7 @@ public class ControlFlow {
|
||||
|
||||
for (int i = 0; i < instructions.size(); i++) {
|
||||
Instruction instruction = instructions.get(i);
|
||||
result.append(Integer.toString(i)).append(": ").append(instruction.toString());
|
||||
result.append(i).append(": ").append(instruction.toString());
|
||||
result.append("\n");
|
||||
}
|
||||
return result.toString();
|
||||
|
||||
+39
-25
@@ -24,7 +24,6 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.light.LightVariableBuilder;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.*;
|
||||
@@ -282,7 +281,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
else if (!field.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
// initialize with default value
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(field, false);
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(field);
|
||||
addInstruction(new PushInstruction(dfaVariable, null, true));
|
||||
addInstruction(new PushInstruction(myFactory.getConstFactory().createDefault(field.getType()), null));
|
||||
addInstruction(new AssignInstruction(null, dfaVariable));
|
||||
@@ -297,7 +296,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
|
||||
private void initializeVariable(PsiVariable variable, PsiExpression initializer) {
|
||||
if (DfaUtil.ignoreInitializer(variable)) return;
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(variable, false);
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(variable);
|
||||
addInstruction(new PushInstruction(dfaVariable, initializer, true));
|
||||
initializer.accept(this);
|
||||
generateBoxingUnboxingInstructionFor(initializer, variable.getType());
|
||||
@@ -403,7 +402,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
@NotNull
|
||||
private List<DfaVariableValue> getVariablesInside(PsiElement exitedStatement) {
|
||||
return ContainerUtil.map(PsiTreeUtil.findChildrenOfType(exitedStatement, PsiVariable.class),
|
||||
var -> myFactory.getVarFactory().createVariableValue(var, false));
|
||||
myFactory.getVarFactory()::createVariableValue);
|
||||
}
|
||||
|
||||
@Override public void visitContinueStatement(PsiContinueStatement statement) {
|
||||
@@ -492,7 +491,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
|
||||
ControlFlow.ControlFlowOffset offset = myCurrentFlow.getNextOffset();
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(parameter, false);
|
||||
DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(parameter);
|
||||
addInstruction(new FlushVariableInstruction(dfaVariable));
|
||||
|
||||
if (!hasSizeCheck) {
|
||||
@@ -606,22 +605,23 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
if (end == null || end == Long.MAX_VALUE || end == Integer.MAX_VALUE) return false;
|
||||
}
|
||||
PsiExpression initializer = loop.getInitializer();
|
||||
if (!PsiType.INT.equals(initializer.getType()) && !PsiType.LONG.equals(initializer.getType())) return false;
|
||||
PsiType type = initializer.getType();
|
||||
if (!PsiType.INT.equals(type) && !PsiType.LONG.equals(type)) return false;
|
||||
DfaValue origin = null;
|
||||
Object initialValue = ExpressionUtils.computeConstantExpression(initializer);
|
||||
if (initialValue instanceof Number) {
|
||||
origin = myFactory.getConstFactory().createFromValue(initialValue, initializer.getType(), null);
|
||||
origin = myFactory.getConstFactory().createFromValue(initialValue, type, null);
|
||||
}
|
||||
else if (initializer instanceof PsiReferenceExpression) {
|
||||
PsiVariable initialVariable = ObjectUtils.tryCast(((PsiReferenceExpression)initializer).resolve(), PsiVariable.class);
|
||||
if ((initialVariable instanceof PsiLocalVariable || initialVariable instanceof PsiParameter)
|
||||
&& !VariableAccessUtils.variableIsAssigned(initialVariable, statement.getBody())) {
|
||||
origin = myFactory.getVarFactory().createVariableValue(initialVariable, false);
|
||||
origin = myFactory.getVarFactory().createVariableValue(initialVariable);
|
||||
}
|
||||
}
|
||||
if (origin == null) return false;
|
||||
long diff = start == null || end == null ? -1 : end - start;
|
||||
DfaVariableValue loopVar = myFactory.getVarFactory().createVariableValue(counter, false);
|
||||
DfaVariableValue loopVar = myFactory.getVarFactory().createVariableValue(counter);
|
||||
addInstruction(new PushInstruction(loopVar, null, true));
|
||||
if(diff >= 0 && diff <= MAX_UNROLL_SIZE) {
|
||||
// Unroll small loops
|
||||
@@ -718,7 +718,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
|
||||
if (myInlinedBlockContext != null) {
|
||||
if (returnValue != null) {
|
||||
DfaVariableValue var = myFactory.getVarFactory().createVariableValue(myInlinedBlockContext.myTarget, false);
|
||||
DfaVariableValue var = myInlinedBlockContext.myTarget;
|
||||
addInstruction(new PushInstruction(var, null, true));
|
||||
returnValue.accept(this);
|
||||
generateBoxingUnboxingInstructionFor(returnValue, var.getVariableType());
|
||||
@@ -1085,7 +1085,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
if (parent instanceof PsiVariable) {
|
||||
// initialization
|
||||
return getFactory().getVarFactory().createVariableValue((PsiVariable)parent, false);
|
||||
return getFactory().getVarFactory().createVariableValue((PsiVariable)parent);
|
||||
}
|
||||
if (parent instanceof PsiAssignmentExpression) {
|
||||
PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent;
|
||||
@@ -1113,13 +1113,14 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
DfaVariableValue var = getTargetVariable(expression);
|
||||
DfaVariableValue arrayWriteTarget = var;
|
||||
if (var == null) {
|
||||
var = getFactory().getVarFactory().createVariableValue(createTempVariable(type), false);
|
||||
var = createTempVariable(type);
|
||||
}
|
||||
PsiExpression[] initializers = expression.getInitializers();
|
||||
DfaExpressionFactory expressionFactory = myFactory.getExpressionFactory();
|
||||
if (arrayWriteTarget != null) {
|
||||
PsiVariable arrayVariable = (PsiVariable)arrayWriteTarget.getPsiVariable();
|
||||
if ((arrayVariable instanceof PsiField && !arrayVariable.hasModifierProperty(PsiModifier.FINAL)) ||
|
||||
if (arrayWriteTarget.isFlushableByCalls() ||
|
||||
arrayVariable == null ||
|
||||
VariableAccessUtils.variableIsUsed(arrayVariable, expression) ||
|
||||
ExpressionUtils.getConstantArrayElements(arrayVariable) != null ||
|
||||
!(expressionFactory.getArrayElementValue(arrayWriteTarget, 0) instanceof DfaVariableValue)) {
|
||||
@@ -1675,7 +1676,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
DfaVariableValue var = getTargetVariable(expression);
|
||||
if (var == null) {
|
||||
var = getFactory().getVarFactory().createVariableValue(createTempVariable(type), false);
|
||||
var = createTempVariable(type);
|
||||
}
|
||||
DfaValue length = SpecialField.ARRAY_LENGTH.createValue(getFactory(), var);
|
||||
addInstruction(new PushInstruction(length, null, true));
|
||||
@@ -1913,7 +1914,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
* @param resultNullness desired nullness returned by block return statement
|
||||
* @param target a variable to store the block result (returned via {@code return} statement)
|
||||
*/
|
||||
void inlineBlock(@NotNull PsiCodeBlock block, @NotNull Nullness resultNullness, @NotNull PsiVariable target) {
|
||||
void inlineBlock(@NotNull PsiCodeBlock block, @NotNull Nullness resultNullness, @NotNull DfaVariableValue target) {
|
||||
InlinedBlockContext oldBlock = myInlinedBlockContext;
|
||||
// Transfer value is pushed to avoid emptying stack beyond this point
|
||||
addInstruction(new PushInstruction(myFactory.controlTransfer(ReturnTransfer.INSTANCE, this.myTrapStack), null));
|
||||
@@ -1931,17 +1932,17 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temporary {@link PsiVariable} (not declared in the original code) to be used within this control flow.
|
||||
* Create a synthetic variable (not declared in the original code) to be used within this control flow.
|
||||
*
|
||||
* @param type a type of variable to create
|
||||
* @return newly created variable
|
||||
*/
|
||||
@NotNull
|
||||
PsiVariable createTempVariable(@Nullable PsiType type) {
|
||||
DfaVariableValue createTempVariable(@Nullable PsiType type) {
|
||||
if(type == null) {
|
||||
type = PsiType.VOID;
|
||||
}
|
||||
return new TempVariable(getInstructionCount(), type, getContext());
|
||||
return getFactory().getVarFactory().createVariableValue(new Synthetic(getInstructionCount()), type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1950,22 +1951,35 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
* @param variable to check
|
||||
* @return true if supplied variable is a temp variable.
|
||||
*/
|
||||
public static boolean isTempVariable(PsiModifierListOwner variable) {
|
||||
return variable instanceof TempVariable;
|
||||
public static boolean isTempVariable(@NotNull DfaVariableValue variable) {
|
||||
return variable.getSource() instanceof Synthetic;
|
||||
}
|
||||
|
||||
private static class TempVariable extends LightVariableBuilder<TempVariable> {
|
||||
TempVariable(int index, @NotNull PsiType type, @NotNull PsiElement navigationElement) {
|
||||
super("tmp$" + index, type, navigationElement);
|
||||
private static final class Synthetic implements DfaVariableSource {
|
||||
private final int myLocation;
|
||||
|
||||
public Synthetic(int location) {
|
||||
myLocation = location;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "tmp$" + myLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InlinedBlockContext {
|
||||
final PsiCodeBlock myCodeBlock;
|
||||
final boolean myForceNonNullBlockResult;
|
||||
final PsiVariable myTarget;
|
||||
final DfaVariableValue myTarget;
|
||||
|
||||
public InlinedBlockContext(PsiCodeBlock codeBlock, boolean forceNonNullBlockResult, PsiVariable target) {
|
||||
public InlinedBlockContext(PsiCodeBlock codeBlock, boolean forceNonNullBlockResult, DfaVariableValue target) {
|
||||
myCodeBlock = codeBlock;
|
||||
myForceNonNullBlockResult = forceNonNullBlockResult;
|
||||
myTarget = target;
|
||||
|
||||
@@ -100,7 +100,7 @@ public abstract class DfaFactType<T> extends Key<T> {
|
||||
@Override
|
||||
Mutability calcFromVariable(@NotNull DfaVariableValue value) {
|
||||
PsiModifierListOwner variable = value.getPsiVariable();
|
||||
return Mutability.getMutability(variable);
|
||||
return variable == null ? Mutability.UNKNOWN : Mutability.getMutability(variable);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -145,16 +145,12 @@ public abstract class DfaFactType<T> extends Key<T> {
|
||||
@Nullable
|
||||
@Override
|
||||
LongRangeSet calcFromVariable(@NotNull DfaVariableValue var) {
|
||||
if (var.getQualifier() != null) {
|
||||
for (SpecialField sf : SpecialField.values()) {
|
||||
if (sf.isMyAccessor(var.getPsiVariable())) {
|
||||
return sf.getRange();
|
||||
}
|
||||
}
|
||||
DfaVariableSource source = var.getSource();
|
||||
if(source instanceof SpecialField) {
|
||||
return ((SpecialField)source).getRange();
|
||||
}
|
||||
PsiModifierListOwner psiVariable = var.getPsiVariable();
|
||||
LongRangeSet fromType = LongRangeSet.fromType(var.getVariableType());
|
||||
return fromType == null ? null : LongRangeSet.fromPsiElement(psiVariable).intersect(fromType);
|
||||
return fromType == null ? null : LongRangeSet.fromPsiElement(var.getPsiVariable()).intersect(fromType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+8
-20
@@ -320,14 +320,12 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
if (!(dfaValue instanceof DfaVariableValue)) return classIndex;
|
||||
DfaVariableValue variableValue = (DfaVariableValue)dfaValue;
|
||||
DfaVariableValue qualifier = variableValue.getQualifier();
|
||||
PsiModifierListOwner variable = variableValue.getPsiVariable();
|
||||
if (qualifier == null) return classIndex;
|
||||
Integer index = getOrCreateEqClassIndex(qualifier);
|
||||
if (index == null) return classIndex;
|
||||
for (DfaValue eqQualifier : myEqClasses.get(index).getMemberValues()) {
|
||||
if (eqQualifier != qualifier && eqQualifier instanceof DfaVariableValue) {
|
||||
DfaVariableValue eqValue = getFactory().getVarFactory()
|
||||
.createVariableValue(variable, variableValue.getVariableType(), variableValue.isNegated(), (DfaVariableValue)eqQualifier);
|
||||
DfaVariableValue eqValue = variableValue.withQualifier((DfaVariableValue)eqQualifier);
|
||||
int i = getEqClassIndex(eqValue);
|
||||
if (i != -1) {
|
||||
uniteClasses(i, classIndex);
|
||||
@@ -639,7 +637,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
getConstantValue(var) == null &&
|
||||
StreamEx.of(getEquivalentValues(var)).without(var).select(DfaVariableValue.class).findFirst().isPresent();
|
||||
List<DfaVariableValue> values = StreamEx.ofKeys(myVariableStates)
|
||||
.filter(var -> ControlFlowAnalyzer.isTempVariable(var.getPsiVariable()))
|
||||
.filter(ControlFlowAnalyzer::isTempVariable)
|
||||
.remove(sharesState)
|
||||
.toList();
|
||||
values.forEach(this::flushVariable);
|
||||
@@ -876,16 +874,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
setVariableState(dfaVar, getVariableState(dfaVar).withFact(DfaFactType.CAN_BE_NULL, true));
|
||||
return;
|
||||
}
|
||||
PsiType psiType;
|
||||
if (constValue instanceof PsiVariable) {
|
||||
psiType = ((PsiVariable)constValue).getType();
|
||||
}
|
||||
else {
|
||||
PsiModifierListOwner context = dfaVar.getPsiVariable();
|
||||
psiType = JavaPsiFacade.getElementFactory(context.getProject())
|
||||
.createTypeByFQClassName(constValue.getClass().getName(), context.getResolveScope());
|
||||
}
|
||||
DfaPsiType dfaType = myFactory.createDfaType(psiType);
|
||||
DfaPsiType dfaType = myFactory.createDfaType(((DfaConstValue)value).getType());
|
||||
DfaVariableState state = getVariableState(dfaVar).withInstanceofValue(dfaType);
|
||||
if (state != null) {
|
||||
setVariableState(dfaVar, state);
|
||||
@@ -1136,10 +1125,10 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
DfaVariableValue qualifier = var.getQualifier();
|
||||
if (qualifier != null) {
|
||||
return StreamEx.of(SpecialField.values())
|
||||
.filter(sf -> sf.isMyAccessor(var.getPsiVariable()))
|
||||
.map(sf -> sf.createValue(myFactory, qualifier))
|
||||
.nonNull().findFirst().orElse(var);
|
||||
DfaValue value = SpecialField.tryCreateValue(qualifier, var.getPsiVariable());
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return var;
|
||||
}
|
||||
@@ -1184,8 +1173,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
if (qualifierIndex == -1) return StreamEx.empty();
|
||||
return StreamEx.of(myEqClasses.get(qualifierIndex).getMemberValues())
|
||||
.without(qualifier).select(DfaVariableValue.class)
|
||||
.map(eqQualifier -> getFactory().getVarFactory()
|
||||
.createVariableValue(var.getPsiVariable(), var.getVariableType(), var.isNegated(), eqQualifier));
|
||||
.map(var::withQualifier);
|
||||
}
|
||||
|
||||
private DfaVariableState findVariableState(DfaVariableValue var) {
|
||||
|
||||
@@ -352,7 +352,7 @@ public class DfaPsiUtil {
|
||||
if (isCallExposingNonInitializedFields(instruction) ||
|
||||
instruction instanceof ReturnInstruction && !((ReturnInstruction)instruction).isViaException()) {
|
||||
for (PsiField field : containingClass.getFields()) {
|
||||
if (!instructionState.getMemoryState().isNotNull(getFactory().getVarFactory().createVariableValue(field, false))) {
|
||||
if (!instructionState.getMemoryState().isNotNull(getFactory().getVarFactory().createVariableValue(field))) {
|
||||
map.put(field, false);
|
||||
} else if (!map.containsKey(field)) {
|
||||
map.put(field, true);
|
||||
|
||||
+1
-3
@@ -28,7 +28,6 @@ import com.intellij.util.PairFunction;
|
||||
import com.intellij.util.containers.*;
|
||||
import com.intellij.util.containers.Queue;
|
||||
import one.util.streamex.IntStreamEx;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -209,8 +208,7 @@ public class LiveVariablesAnalyzer {
|
||||
for (FinishElementInstruction instruction : toFlush.keySet()) {
|
||||
Collection<DfaVariableValue> values = toFlush.get(instruction);
|
||||
// Do not flush special values as they could be used implicitly
|
||||
values.removeIf(var -> var.getQualifier() != null &&
|
||||
StreamEx.of(SpecialField.values()).anyMatch(sf -> sf.isMyAccessor(var.getPsiVariable())));
|
||||
values.removeIf(var -> var.getSource() instanceof SpecialField);
|
||||
instruction.getVarsToFlush().addAll(values);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -98,7 +98,7 @@ class NullParameterConstraintChecker extends DataFlowRunner {
|
||||
if (instruction instanceof ReturnInstruction && !((ReturnInstruction)instruction).isViaException()) {
|
||||
DfaMemoryState memState = instructionState.getMemoryState();
|
||||
for (PsiParameter parameter : myPossiblyViolatedParameters.toArray(PsiParameter.EMPTY_ARRAY)) {
|
||||
final DfaVariableValue dfaVar = getFactory().getVarFactory().createVariableValue(parameter, false);
|
||||
final DfaVariableValue dfaVar = getFactory().getVarFactory().createVariableValue(parameter);
|
||||
if (memState.isNotNull(dfaVar)) {
|
||||
myParametersWithSuccessfulExecutionInNotNullState.add(parameter);
|
||||
}
|
||||
@@ -122,7 +122,7 @@ class NullParameterConstraintChecker extends DataFlowRunner {
|
||||
protected MyDfaMemoryState(DfaValueFactory factory) {
|
||||
super(factory);
|
||||
for (PsiParameter parameter : myPossiblyViolatedParameters) {
|
||||
setVariableState(getFactory().getVarFactory().createVariableValue(parameter, false),
|
||||
setVariableState(getFactory().getVarFactory().createVariableValue(parameter),
|
||||
new DfaVariableState(DfaFactMap.EMPTY.with(DfaFactType.CAN_BE_NULL, true)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public class NullnessUtil {
|
||||
if (nullability != Nullness.UNKNOWN) {
|
||||
return toBoolean(nullability);
|
||||
}
|
||||
if (var == null) return null;
|
||||
|
||||
Nullness defaultNullability = value.getFactory().suggestNullabilityForNonAnnotatedMember(var);
|
||||
|
||||
|
||||
+15
-41
@@ -3,14 +3,11 @@ package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.MethodUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -22,7 +19,7 @@ import static com.intellij.codeInspection.dataFlow.MethodContract.ValueConstrain
|
||||
*
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
public enum SpecialField {
|
||||
public enum SpecialField implements DfaVariableSource {
|
||||
ARRAY_LENGTH(null, "length", true, LongRangeSet.indexRange()) {
|
||||
@Override
|
||||
public boolean isMyAccessor(PsiModifierListOwner accessor) {
|
||||
@@ -51,20 +48,6 @@ public enum SpecialField {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
PsiModifierListOwner getCanonicalOwner(@Nullable PsiModifierListOwner qualifier, @Nullable PsiClass psiClass) {
|
||||
if (qualifier == null) return null;
|
||||
PsiClass arrayClass = JavaPsiFacade.getElementFactory(qualifier.getProject())
|
||||
.getArrayClass(PsiUtil.getLanguageLevel(qualifier));
|
||||
return arrayClass.findFieldByName("length", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Array.length";
|
||||
}
|
||||
},
|
||||
STRING_LENGTH(CommonClassNames.JAVA_LANG_STRING, "length", true, LongRangeSet.indexRange()) {
|
||||
@Override
|
||||
@@ -96,7 +79,8 @@ public enum SpecialField {
|
||||
myRange = range;
|
||||
}
|
||||
|
||||
public boolean isFinal() {
|
||||
@Override
|
||||
public boolean isStable() {
|
||||
return myFinal;
|
||||
}
|
||||
|
||||
@@ -118,23 +102,17 @@ public enum SpecialField {
|
||||
return accessor instanceof PsiMethod && MethodUtils.methodMatches((PsiMethod)accessor, myClassName, null, myMethodName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a canonical accessor which can be used to read this special field
|
||||
*
|
||||
* @param qualifier a qualifier accessor (if known)
|
||||
* @param psiClass a class for which the canonical method should be resolved
|
||||
* @return a canonical accessor representing this special field or null if cannot be determined.
|
||||
*/
|
||||
@Nullable
|
||||
PsiModifierListOwner getCanonicalOwner(@Nullable PsiModifierListOwner qualifier, @Nullable PsiClass psiClass) {
|
||||
if (psiClass == null) return null;
|
||||
if (!myClassName.equals(psiClass.getQualifiedName())) {
|
||||
PsiClass myClass = JavaPsiFacade.getInstance(psiClass.getProject()).findClass(myClassName, psiClass.getResolveScope());
|
||||
if (!InheritanceUtil.isInheritorOrSelf(psiClass, myClass, true)) return null;
|
||||
psiClass = myClass;
|
||||
public static DfaValue tryCreateValue(DfaValue qualifier, PsiElement element) {
|
||||
if (qualifier == null) return null;
|
||||
DfaValueFactory factory = qualifier.getFactory();
|
||||
if (factory == null) return null;
|
||||
if (!(element instanceof PsiVariable) && !(element instanceof PsiMethod)) return null;
|
||||
for (SpecialField field : values()) {
|
||||
if (field.isMyAccessor((PsiModifierListOwner)element)) {
|
||||
return field.createValue(factory, qualifier);
|
||||
}
|
||||
}
|
||||
PsiMethod[] methods = psiClass.findMethodsByName(myMethodName, false);
|
||||
return methods.length == 1 ? methods[0] : null;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,11 +138,7 @@ public enum SpecialField {
|
||||
}
|
||||
}
|
||||
}
|
||||
PsiModifierListOwner owner =
|
||||
getCanonicalOwner(psiVariable, PsiUtil.resolveClassInClassTypeOnly(variableValue.getVariableType()));
|
||||
if (owner != null) {
|
||||
return factory.getVarFactory().createVariableValue(owner, PsiType.INT, false, variableValue);
|
||||
}
|
||||
return factory.getVarFactory().createVariableValue(this, PsiType.INT, variableValue);
|
||||
}
|
||||
if(qualifier instanceof DfaConstValue) {
|
||||
Object obj = ((DfaConstValue)qualifier).getValue();
|
||||
@@ -205,6 +179,6 @@ public enum SpecialField {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringUtil.getShortName(myClassName)+"."+myMethodName+"()";
|
||||
return myMethodName;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-9
@@ -503,15 +503,13 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
|
||||
if (methodType == MethodCallInstruction.MethodType.METHOD_REFERENCE_CALL && qualifierValue instanceof DfaVariableValue) {
|
||||
PsiMethod method = instruction.getTargetMethod();
|
||||
for (SpecialField sf : SpecialField.values()) {
|
||||
if (sf.isMyAccessor(method)) {
|
||||
return sf.createValue(factory, qualifierValue);
|
||||
}
|
||||
DfaValue value = SpecialField.tryCreateValue(qualifierValue, method);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
PsiModifierListOwner modifierListOwner = DfaExpressionFactory.getAccessedVariableOrGetter(method);
|
||||
if (modifierListOwner != null) {
|
||||
return factory.getVarFactory().createVariableValue(modifierListOwner, instruction.getResultType(), false,
|
||||
(DfaVariableValue)qualifierValue);
|
||||
DfaVariableSource source = DfaExpressionFactory.getAccessedVariableOrGetter(method);
|
||||
if (source != null) {
|
||||
return factory.getVarFactory().createVariableValue(source, instruction.getResultType(), (DfaVariableValue)qualifierValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,7 +524,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
|
||||
if (methodType == MethodCallInstruction.MethodType.CAST) {
|
||||
assert qualifierValue != null;
|
||||
if (qualifierValue instanceof DfaConstValue) {
|
||||
if (qualifierValue instanceof DfaConstValue && type != null) {
|
||||
Object casted = TypeConversionUtil.computeCastTo(((DfaConstValue)qualifierValue).getValue(), type);
|
||||
return factory.getConstFactory().createFromValue(casted, type, ((DfaConstValue)qualifierValue).getConstant());
|
||||
}
|
||||
|
||||
@@ -145,12 +145,12 @@ private class ControlTransferHandler(val state: DfaMemoryState, val runner: Data
|
||||
|
||||
private fun stateForCatchClause(param: PsiParameter, varState: DfaVariableState): DfaMemoryState {
|
||||
val catchingCopy = state.createCopy() as DfaMemoryStateImpl
|
||||
catchingCopy.setVariableState(catchingCopy.factory.varFactory.createVariableValue(param, false), varState)
|
||||
catchingCopy.setVariableState(catchingCopy.factory.varFactory.createVariableValue(param), varState)
|
||||
return catchingCopy
|
||||
}
|
||||
|
||||
private fun initVariableState(param: PsiParameter, throwable: DfaPsiType?): DfaVariableState {
|
||||
val sampleVar = (state as DfaMemoryStateImpl).factory.varFactory.createVariableValue(param, false)
|
||||
val sampleVar = (state as DfaMemoryStateImpl).factory.varFactory.createVariableValue(param)
|
||||
val varState = state.createVariableState(sampleVar).withFact(DfaFactType.CAN_BE_NULL, false)
|
||||
return if (throwable != null) varState.withInstanceofValue(throwable)!! else varState
|
||||
}
|
||||
|
||||
+2
-4
@@ -21,7 +21,6 @@ import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.psi.PsiMethodCallExpression;
|
||||
import com.intellij.psi.PsiVariable;
|
||||
import com.siyeh.ig.callMatcher.CallMapper;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.MethodCallUtils;
|
||||
@@ -104,9 +103,8 @@ public class CollectionFactoryInliner implements CallInliner {
|
||||
if (factoryInfo.mySize == -1) {
|
||||
builder.push(result);
|
||||
} else {
|
||||
PsiVariable variable = builder.createTempVariable(call.getType());
|
||||
DfaVariableValue variableValue = factory.getVarFactory().createVariableValue(variable, false);
|
||||
builder.pushVariable(variable) // tmpVar = <Value of collection type>
|
||||
DfaVariableValue variableValue = builder.createTempVariable(call.getType());
|
||||
builder.pushForWrite(variableValue) // tmpVar = <Value of collection type>
|
||||
.push(result)
|
||||
.assign() // leave tmpVar on stack: it's result of method call
|
||||
.push(factoryInfo.mySizeField.createValue(factory, variableValue)) // tmpVar.size = <size>
|
||||
|
||||
+6
-6
@@ -42,12 +42,12 @@ public class LambdaInliner implements CallInliner {
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
PsiParameter[] parameters = lambda.getParameterList().getParameters();
|
||||
if (args.length != parameters.length) return false;
|
||||
EntryStream.zip(args, parameters).forKeyValue((arg, parameter) ->
|
||||
builder.pushVariable(parameter)
|
||||
.pushExpression(arg)
|
||||
.boxUnbox(arg, parameter.getType())
|
||||
.assign()
|
||||
.pop());
|
||||
EntryStream.zip(args, parameters).forKeyValue(
|
||||
(arg, parameter) -> builder.pushForWrite(builder.getFactory().getVarFactory().createVariableValue(parameter))
|
||||
.pushExpression(arg)
|
||||
.boxUnbox(arg, parameter.getType())
|
||||
.assign()
|
||||
.pop());
|
||||
builder.inlineLambda(lambda, Nullness.UNKNOWN);
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -234,7 +234,7 @@ public class OptionalChainInliner implements CallInliner {
|
||||
return;
|
||||
}
|
||||
// Restore stack for common invokeFunction
|
||||
StreamEx.of(parameters).forEach(p -> builder.push(builder.getFactory().getVarFactory().createVariableValue(p, false)));
|
||||
StreamEx.of(parameters).map(builder.getFactory().getVarFactory()::createVariableValue).forEach(builder::push);
|
||||
}
|
||||
}
|
||||
builder
|
||||
|
||||
+11
-10
@@ -17,6 +17,7 @@ package com.intellij.codeInspection.dataFlow.inliner;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
@@ -190,7 +191,7 @@ public class StreamChainInliner implements CallInliner {
|
||||
}
|
||||
|
||||
static abstract class TerminalStep extends Step {
|
||||
PsiVariable myResult;
|
||||
DfaVariableValue myResult;
|
||||
|
||||
TerminalStep(@NotNull PsiMethodCallExpression call, PsiExpression function) {
|
||||
super(call, null, function);
|
||||
@@ -199,7 +200,7 @@ public class StreamChainInliner implements CallInliner {
|
||||
@Override
|
||||
void before(CFGBuilder builder) {
|
||||
myResult = builder.createTempVariable(myCall.getType());
|
||||
builder.pushVariable(myResult)
|
||||
builder.pushForWrite(myResult)
|
||||
.chain(this::pushInitialValue)
|
||||
.assign()
|
||||
.pop()
|
||||
@@ -210,7 +211,7 @@ public class StreamChainInliner implements CallInliner {
|
||||
|
||||
@Override
|
||||
void pushResult(CFGBuilder builder) {
|
||||
builder.push(builder.getFactory().getVarFactory().createVariableValue(myResult, false));
|
||||
builder.push(myResult);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,12 +237,12 @@ public class StreamChainInliner implements CallInliner {
|
||||
if (!(type instanceof PsiPrimitiveType)) {
|
||||
type = PsiPrimitiveType.getUnboxedType(type);
|
||||
}
|
||||
builder.push(builder.getFactory().getConstFactory().createDefault(type));
|
||||
builder.push(builder.getFactory().getConstFactory().createDefault(Objects.requireNonNull(type)));
|
||||
}
|
||||
|
||||
@Override
|
||||
void iteration(CFGBuilder builder) {
|
||||
builder.pushVariable(myResult).pushUnknown().assign().splice(2);
|
||||
builder.pushForWrite(myResult).pushUnknown().assign().splice(2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +261,7 @@ public class StreamChainInliner implements CallInliner {
|
||||
if (myFunction != null) {
|
||||
builder.pushUnknown().invokeFunction(2, myFunction);
|
||||
}
|
||||
builder.pushVariable(myResult).push(builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, true)).assign().splice(2);
|
||||
builder.pushForWrite(myResult).push(builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, true)).assign().splice(2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +287,7 @@ public class StreamChainInliner implements CallInliner {
|
||||
@Override
|
||||
void iteration(CFGBuilder builder) {
|
||||
myComparatorModel.invoke(builder);
|
||||
builder.pushVariable(myResult).push(builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, true)).assign().pop();
|
||||
builder.pushForWrite(myResult).push(builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, true)).assign().pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -309,7 +310,7 @@ public class StreamChainInliner implements CallInliner {
|
||||
void iteration(CFGBuilder builder) {
|
||||
builder.invokeFunction(1, myFunction)
|
||||
.ifConditionIs(!"allMatch".equals(myCall.getMethodExpression().getReferenceName()))
|
||||
.pushVariable(myResult)
|
||||
.pushForWrite(myResult)
|
||||
.push(builder.getFactory().getBoolean("anyMatch".equals(myCall.getMethodExpression().getReferenceName())))
|
||||
.assign()
|
||||
.pop()
|
||||
@@ -645,7 +646,7 @@ public class StreamChainInliner implements CallInliner {
|
||||
.evaluateFunction(fn)
|
||||
.chain(firstStep::before)
|
||||
.doWhile()
|
||||
.pushVariable(builder.createTempVariable(inType))
|
||||
.pushForWrite(builder.createTempVariable(inType))
|
||||
.invokeFunction(0, fn)
|
||||
.assign()
|
||||
.chain(firstStep::iteration)
|
||||
@@ -731,7 +732,7 @@ public class StreamChainInliner implements CallInliner {
|
||||
|
||||
private static void makeMainLoop(CFGBuilder builder, Step firstStep, PsiType inType) {
|
||||
builder.doWhile()
|
||||
.pushVariable(builder.createTempVariable(inType))
|
||||
.pushForWrite(builder.createTempVariable(inType))
|
||||
.push(builder.getFactory().createTypeValue(inType, DfaPsiUtil.getTypeNullability(inType)))
|
||||
.assign()
|
||||
.chain(firstStep::iteration)
|
||||
|
||||
+18
-9
@@ -41,15 +41,16 @@ public class DfaConstValue extends DfaValue {
|
||||
|
||||
Factory(DfaValueFactory factory) {
|
||||
myFactory = factory;
|
||||
dfaNull = new DfaConstValue(null, factory, null);
|
||||
dfaFalse = new DfaConstValue(Boolean.FALSE, factory, null);
|
||||
dfaTrue = new DfaConstValue(Boolean.TRUE, factory, null);
|
||||
dfaFail = new DfaConstValue(ourThrowable, factory, null);
|
||||
dfaNull = new DfaConstValue(null, PsiType.NULL, factory, null);
|
||||
dfaFalse = new DfaConstValue(Boolean.FALSE, PsiType.BOOLEAN, factory, null);
|
||||
dfaTrue = new DfaConstValue(Boolean.TRUE, PsiType.BOOLEAN, factory, null);
|
||||
dfaFail = new DfaConstValue(ourThrowable, PsiType.VOID, factory, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DfaValue create(PsiLiteralExpression expr) {
|
||||
PsiType type = expr.getType();
|
||||
if (type == null) return null;
|
||||
if (PsiType.NULL.equals(type)) return dfaNull;
|
||||
Object value = expr.getValue();
|
||||
if (value == null) return null;
|
||||
@@ -95,18 +96,19 @@ public class DfaConstValue extends DfaValue {
|
||||
* @return a constant (e.g. 0 from int, false for boolean, null for reference type).
|
||||
*/
|
||||
@NotNull
|
||||
public DfaConstValue createDefault(PsiType type) {
|
||||
public DfaConstValue createDefault(@NotNull PsiType type) {
|
||||
return createFromValue(PsiTypesUtil.getDefaultValue(type), type, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaConstValue createFromValue(Object value, final PsiType type, @Nullable PsiVariable constant) {
|
||||
public DfaConstValue createFromValue(Object value, @NotNull PsiType type, @Nullable PsiVariable constant) {
|
||||
if (value == Boolean.TRUE) return dfaTrue;
|
||||
if (value == Boolean.FALSE) return dfaFalse;
|
||||
if (value == null) return dfaNull;
|
||||
|
||||
if (TypeConversionUtil.isNumericType(type) && !TypeConversionUtil.isFloatOrDoubleType(type)) {
|
||||
value = TypeConversionUtil.computeCastTo(value, PsiType.LONG);
|
||||
type = PsiType.LONG;
|
||||
value = TypeConversionUtil.computeCastTo(value, type);
|
||||
}
|
||||
if (value instanceof Double || value instanceof Float) {
|
||||
double doubleValue = ((Number)value).doubleValue();
|
||||
@@ -115,7 +117,7 @@ public class DfaConstValue extends DfaValue {
|
||||
}
|
||||
DfaConstValue instance = myValues.get(value);
|
||||
if (instance == null) {
|
||||
instance = new DfaConstValue(value, myFactory, constant);
|
||||
instance = new DfaConstValue(value, type, myFactory, constant);
|
||||
myValues.put(value, instance);
|
||||
}
|
||||
|
||||
@@ -141,10 +143,12 @@ public class DfaConstValue extends DfaValue {
|
||||
|
||||
private final Object myValue;
|
||||
@Nullable private final PsiVariable myConstant;
|
||||
@NotNull private final PsiType myType;
|
||||
|
||||
private DfaConstValue(Object value, DfaValueFactory factory, @Nullable PsiVariable constant) {
|
||||
private DfaConstValue(Object value, @NotNull PsiType type, DfaValueFactory factory, @Nullable PsiVariable constant) {
|
||||
super(factory);
|
||||
myValue = value;
|
||||
myType = type;
|
||||
myConstant = constant;
|
||||
}
|
||||
|
||||
@@ -154,6 +158,11 @@ public class DfaConstValue extends DfaValue {
|
||||
return myValue.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiType getType() {
|
||||
return myType;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
+149
-41
@@ -26,7 +26,6 @@ import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
|
||||
import com.intellij.psi.impl.light.LightVariableBuilder;
|
||||
import com.intellij.psi.util.PropertyUtilBase;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -62,7 +61,7 @@ public class DfaExpressionFactory {
|
||||
}
|
||||
|
||||
private final DfaValueFactory myFactory;
|
||||
private final Map<Integer, PsiVariable> myMockIndices = ContainerUtil.newHashMap();
|
||||
private final Map<Integer, ArrayElementSource> myMockIndices = ContainerUtil.newHashMap();
|
||||
|
||||
DfaExpressionFactory(DfaValueFactory factory) {
|
||||
myFactory = factory;
|
||||
@@ -122,7 +121,7 @@ public class DfaExpressionFactory {
|
||||
PsiJavaCodeReferenceElement qualifier = ((PsiThisExpression)expression).getQualifier();
|
||||
PsiElement target = qualifier == null ? null : qualifier.resolve();
|
||||
if (target instanceof PsiClass) {
|
||||
return myFactory.getVarFactory().createVariableValue((PsiModifierListOwner)target, null, false, null);
|
||||
return myFactory.getVarFactory().createVariableValue(new ThisSource((PsiClass)target), expression.getType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,30 +133,33 @@ public class DfaExpressionFactory {
|
||||
if (specialValue != null) {
|
||||
return specialValue;
|
||||
}
|
||||
PsiModifierListOwner var = getAccessedVariableOrGetter(refExpr.resolve());
|
||||
DfaVariableSource var = getAccessedVariableOrGetter(refExpr.resolve());
|
||||
if (var == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!var.hasModifierProperty(PsiModifier.VOLATILE)) {
|
||||
if (var instanceof PsiVariable && var.hasModifierProperty(PsiModifier.FINAL) && !PsiUtil.isAccessedForWriting(refExpr)) {
|
||||
DfaValue constValue = myFactory.getConstFactory().create((PsiVariable)var);
|
||||
if (constValue != null && !maybeUninitializedConstant(constValue, refExpr, var)) return constValue;
|
||||
}
|
||||
|
||||
if (ExpressionUtil.isEffectivelyUnqualified(refExpr) || isStaticFinalConstantWithoutInitializationHacks(var) ||
|
||||
(var instanceof PsiMethod && var.hasModifierProperty(PsiModifier.STATIC))) {
|
||||
return myFactory.getVarFactory().createVariableValue(var, refExpr.getType(), false, null);
|
||||
}
|
||||
|
||||
DfaVariableValue qualifier = getQualifierVariable(refExpr.getQualifierExpression());
|
||||
if (qualifier != null) {
|
||||
return myFactory.getVarFactory().createVariableValue(var, refExpr.getType(), false, qualifier);
|
||||
}
|
||||
PsiModifierListOwner psiElement = var.getPsiElement();
|
||||
boolean isVolatile = psiElement != null && psiElement.hasModifierProperty(PsiModifier.VOLATILE);
|
||||
if (isVolatile) {
|
||||
PsiType type = refExpr.getType();
|
||||
return myFactory.createTypeValue(type, DfaPsiUtil.getElementNullability(type, psiElement));
|
||||
}
|
||||
if (psiElement instanceof PsiVariable && psiElement.hasModifierProperty(PsiModifier.FINAL) && !PsiUtil.isAccessedForWriting(refExpr)) {
|
||||
DfaValue constValue = myFactory.getConstFactory().create((PsiVariable)psiElement);
|
||||
if (constValue != null && !maybeUninitializedConstant(constValue, refExpr, psiElement)) return constValue;
|
||||
}
|
||||
if (psiElement != null &&
|
||||
(ExpressionUtil.isEffectivelyUnqualified(refExpr) || isStaticFinalConstantWithoutInitializationHacks(psiElement) ||
|
||||
(psiElement instanceof PsiMethod && psiElement.hasModifierProperty(PsiModifier.STATIC)))) {
|
||||
return myFactory.getVarFactory().createVariableValue(var, refExpr.getType());
|
||||
}
|
||||
DfaVariableValue qualifier = getQualifierVariable(refExpr.getQualifierExpression());
|
||||
if (qualifier != null) {
|
||||
return myFactory.getVarFactory().createVariableValue(var, refExpr.getType(), qualifier);
|
||||
}
|
||||
|
||||
PsiType type = refExpr.getType();
|
||||
return myFactory.createTypeValue(type, DfaPsiUtil.getElementNullability(type, var));
|
||||
return myFactory.createTypeValue(type, DfaPsiUtil.getElementNullability(type, psiElement));
|
||||
}
|
||||
|
||||
private DfaVariableValue getQualifierVariable(PsiExpression qualifierExpression) {
|
||||
@@ -169,7 +171,7 @@ public class DfaExpressionFactory {
|
||||
else if (qualifierValue instanceof DfaConstValue) {
|
||||
Object constValue = ((DfaConstValue)qualifierValue).getValue();
|
||||
if (constValue instanceof PsiVariable) {
|
||||
qualifier = myFactory.getVarFactory().createVariableValue((PsiVariable)constValue, false);
|
||||
qualifier = myFactory.getVarFactory().createVariableValue((PsiVariable)constValue);
|
||||
}
|
||||
}
|
||||
return qualifier;
|
||||
@@ -194,39 +196,30 @@ public class DfaExpressionFactory {
|
||||
@Nullable
|
||||
private DfaValue createFromSpecialField(PsiReferenceExpression refExpr) {
|
||||
PsiElement target = refExpr.resolve();
|
||||
if (!(target instanceof PsiModifierListOwner)) {
|
||||
return null;
|
||||
}
|
||||
for (SpecialField sf : SpecialField.values()) {
|
||||
if (sf.isMyAccessor((PsiModifierListOwner)target)) {
|
||||
DfaVariableValue qualifier = getQualifierVariable(refExpr.getQualifierExpression());
|
||||
if (qualifier != null) {
|
||||
return sf.createValue(myFactory, qualifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
if (!(target instanceof PsiModifierListOwner)) return null;
|
||||
DfaVariableValue qualifier = getQualifierVariable(refExpr.getQualifierExpression());
|
||||
return SpecialField.tryCreateValue(qualifier, target);
|
||||
}
|
||||
|
||||
@Contract("null -> null")
|
||||
@Nullable
|
||||
public static PsiModifierListOwner getAccessedVariableOrGetter(final PsiElement target) {
|
||||
public static DfaVariableSource getAccessedVariableOrGetter(final PsiElement target) {
|
||||
if (target instanceof PsiVariable) {
|
||||
return (PsiVariable)target;
|
||||
return new PlainSource((PsiVariable)target);
|
||||
}
|
||||
if (target instanceof PsiMethod) {
|
||||
PsiMethod method = (PsiMethod)target;
|
||||
if (PropertyUtilBase.isSimplePropertyGetter(method) && ControlFlowAnalyzer.getMethodCallContracts(method, null).isEmpty()) {
|
||||
String qName = PsiUtil.getMemberQualifiedName(method);
|
||||
if (qName == null || !FALSE_GETTERS.value(qName)) {
|
||||
return method;
|
||||
return new GetterSource(method);
|
||||
}
|
||||
}
|
||||
if (method.getParameterList().isEmpty()) {
|
||||
if ((ControlFlowAnalyzer.isPure(method) ||
|
||||
AnnotationUtil.findAnnotation(method.getContainingClass(), "javax.annotation.concurrent.Immutable") != null) &&
|
||||
ControlFlowAnalyzer.getMethodCallContracts(method, null).isEmpty()) {
|
||||
return method;
|
||||
return new GetterSource(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,16 +282,131 @@ public class DfaExpressionFactory {
|
||||
return getAdvancedExpressionDfaValue(constantArrayElement);
|
||||
}
|
||||
}
|
||||
PsiVariable indexVariable = getArrayIndexVariable(arrayPsiVar, index);
|
||||
ArrayElementSource indexVariable = getArrayIndexVariable(index);
|
||||
if (indexVariable == null) return null;
|
||||
return myFactory.getVarFactory().createVariableValue(indexVariable, componentType, false, arrayDfaVar);
|
||||
return myFactory.getVarFactory().createVariableValue(indexVariable, componentType, arrayDfaVar);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiVariable getArrayIndexVariable(@NotNull PsiElement anchor, int index) {
|
||||
private ArrayElementSource getArrayIndexVariable(int index) {
|
||||
if (index >= 0) {
|
||||
return myMockIndices.computeIfAbsent(index, k -> new LightVariableBuilder<>("[" + k + "]", PsiType.INT, anchor));
|
||||
return myMockIndices.computeIfAbsent(index, ArrayElementSource::new);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static final class PlainSource implements DfaVariableSource {
|
||||
private final @NotNull PsiVariable myVariable;
|
||||
|
||||
PlainSource(@NotNull PsiVariable variable) {
|
||||
myVariable = variable;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(myVariable.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiVariable getPsiElement() {
|
||||
return myVariable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStable() {
|
||||
return myVariable instanceof PsiLocalVariable ||
|
||||
myVariable instanceof PsiParameter ||
|
||||
myVariable.hasModifierProperty(PsiModifier.FINAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj == this || obj instanceof PlainSource && ((PlainSource)obj).myVariable == myVariable;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class GetterSource implements DfaVariableSource {
|
||||
private final @NotNull PsiMethod myGetter;
|
||||
|
||||
GetterSource(@NotNull PsiMethod getter) {
|
||||
myGetter = getter;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return myGetter.getName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiMethod getPsiElement() {
|
||||
return myGetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCall() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj == this || (obj instanceof GetterSource && ((GetterSource)obj).myGetter == myGetter);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ArrayElementSource implements DfaVariableSource {
|
||||
private final int myIndex;
|
||||
|
||||
ArrayElementSource(int index) {
|
||||
myIndex = index;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + myIndex + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStable() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ThisSource implements DfaVariableSource {
|
||||
@Nullable
|
||||
private final PsiClass myQualifier;
|
||||
|
||||
ThisSource(@Nullable PsiClass qualifier) {
|
||||
myQualifier = qualifier;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return myQualifier == null ? "this" : myQualifier.getQualifiedName() + ".this";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiModifierListOwner getPsiElement() {
|
||||
return myQualifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return this == obj || obj instanceof ThisSource && ((ThisSource)obj).myQualifier == myQualifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInspection.dataFlow.value;
|
||||
|
||||
import com.intellij.psi.PsiModifierListOwner;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a source of {@link DfaVariableValue}. Two variables are the same if they have the same source, qualifier and negation flag.
|
||||
* A source could be a PsiVariable, getter method, array element with given index, this expression, etc.
|
||||
* <p>
|
||||
* Subclasses must have proper {@link Object#equals(Object)} and implementation or instantiation must be controlled to prevent
|
||||
* creating equal objects. Also {@link #toString()} must return sane representation of the source.
|
||||
*/
|
||||
public interface DfaVariableSource {
|
||||
/**
|
||||
* @return a PSI element associated with given source or null if not applicable
|
||||
*/
|
||||
@Nullable
|
||||
default PsiModifierListOwner getPsiElement() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the value stored in this source cannot be changed implicitly (e.g. inside the unknown method call)
|
||||
*/
|
||||
boolean isStable();
|
||||
|
||||
/**
|
||||
* @return true if the value behind this source is a method call which result might be computed from other sources
|
||||
*/
|
||||
default boolean isCall() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be overridden to return stable string representation of the source.
|
||||
* In particular {@code source1.equals(source2)} implies that {@code source1.toString().equals(source2.toString())}
|
||||
*/
|
||||
@Override
|
||||
String toString();
|
||||
}
|
||||
+56
-34
@@ -16,10 +16,16 @@
|
||||
|
||||
package com.intellij.codeInspection.dataFlow.value;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.*;
|
||||
import com.intellij.codeInspection.dataFlow.DfaFactMap;
|
||||
import com.intellij.codeInspection.dataFlow.DfaFactType;
|
||||
import com.intellij.codeInspection.dataFlow.Nullness;
|
||||
import com.intellij.codeInspection.dataFlow.NullnessUtil;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Trinity;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.PsiEllipsisType;
|
||||
import com.intellij.psi.PsiModifierListOwner;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.PsiVariable;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.SmartList;
|
||||
@@ -27,7 +33,6 @@ import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class DfaVariableValue extends DfaValue {
|
||||
@@ -41,24 +46,37 @@ public class DfaVariableValue extends DfaValue {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaVariableValue createVariableValue(PsiVariable myVariable, boolean isNegated) {
|
||||
PsiType varType = myVariable.getType();
|
||||
public DfaVariableValue createVariableValue(PsiVariable variable) {
|
||||
PsiType varType = variable.getType();
|
||||
if (varType instanceof PsiEllipsisType) {
|
||||
varType = new PsiArrayType(((PsiEllipsisType)varType).getComponentType());
|
||||
varType = ((PsiEllipsisType)varType).toArrayType();
|
||||
}
|
||||
return createVariableValue(myVariable, varType, isNegated, null);
|
||||
return createVariableValue(new DfaExpressionFactory.PlainSource(variable), varType);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaVariableValue createVariableValue(@NotNull PsiModifierListOwner myVariable,
|
||||
public DfaVariableValue createVariableValue(@NotNull DfaVariableSource source, @Nullable PsiType varType) {
|
||||
return createVariableValue(source, varType, false, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaVariableValue createVariableValue(@NotNull DfaVariableSource source,
|
||||
@Nullable PsiType varType,
|
||||
boolean isNegated,
|
||||
@Nullable DfaVariableValue qualifier) {
|
||||
Trinity<Boolean,String,DfaVariableValue> key = Trinity.create(isNegated, ((PsiNamedElement)myVariable).getName(), qualifier);
|
||||
return createVariableValue(source, varType, false, qualifier);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
DfaVariableValue createVariableValue(@NotNull DfaVariableSource source,
|
||||
@Nullable PsiType varType,
|
||||
boolean isNegated,
|
||||
@Nullable DfaVariableValue qualifier) {
|
||||
Trinity<Boolean, String, DfaVariableValue> key = Trinity.create(isNegated, source.toString(), qualifier);
|
||||
for (DfaVariableValue aVar : myExistingVars.get(key)) {
|
||||
if (aVar.hardEquals(myVariable, varType, isNegated, qualifier)) return aVar;
|
||||
if (aVar.hardEquals(source, varType, isNegated, qualifier)) return aVar;
|
||||
}
|
||||
|
||||
DfaVariableValue result = new DfaVariableValue(myVariable, varType, isNegated, myFactory, qualifier);
|
||||
DfaVariableValue result = new DfaVariableValue(source, varType, isNegated, myFactory, qualifier);
|
||||
myExistingVars.putValue(key, result);
|
||||
while (qualifier != null) {
|
||||
qualifier.myDependents.add(result);
|
||||
@@ -73,7 +91,7 @@ public class DfaVariableValue extends DfaValue {
|
||||
}
|
||||
}
|
||||
|
||||
private final PsiModifierListOwner myVariable;
|
||||
@NotNull private final DfaVariableSource mySource;
|
||||
private final PsiType myVarType;
|
||||
@Nullable private final DfaVariableValue myQualifier;
|
||||
private DfaVariableValue myNegatedValue;
|
||||
@@ -82,15 +100,19 @@ public class DfaVariableValue extends DfaValue {
|
||||
private final DfaPsiType myDfaType;
|
||||
private final List<DfaVariableValue> myDependents = new SmartList<>();
|
||||
|
||||
private DfaVariableValue(@NotNull PsiModifierListOwner variable, @Nullable PsiType varType, boolean isNegated, DfaValueFactory factory, @Nullable DfaVariableValue qualifier) {
|
||||
private DfaVariableValue(@NotNull DfaVariableSource source,
|
||||
@Nullable PsiType varType,
|
||||
boolean isNegated,
|
||||
DfaValueFactory factory,
|
||||
@Nullable DfaVariableValue qualifier) {
|
||||
super(factory);
|
||||
myVariable = variable;
|
||||
mySource = source;
|
||||
myIsNegated = isNegated;
|
||||
myQualifier = qualifier;
|
||||
myVarType = varType;
|
||||
myDfaType = varType == null ? null : myFactory.createDfaType(varType);
|
||||
if (varType != null && !varType.isValid()) {
|
||||
PsiUtil.ensureValidType(varType, "Variable: " + variable + " of class " + variable.getClass());
|
||||
PsiUtil.ensureValidType(varType, "Variable: " + source + " of class " + source.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,9 +121,14 @@ public class DfaVariableValue extends DfaValue {
|
||||
return myDfaType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Nullable
|
||||
public PsiModifierListOwner getPsiVariable() {
|
||||
return myVariable;
|
||||
return mySource.getPsiElement();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaVariableSource getSource() {
|
||||
return mySource;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -123,16 +150,21 @@ public class DfaVariableValue extends DfaValue {
|
||||
if (myNegatedValue != null) {
|
||||
return myNegatedValue;
|
||||
}
|
||||
return myNegatedValue = myFactory.getVarFactory().createVariableValue(myVariable, myVarType, !myIsNegated, myQualifier);
|
||||
return myNegatedValue = myFactory.getVarFactory().createVariableValue(mySource, myVarType, !myIsNegated, myQualifier);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaVariableValue withQualifier(DfaVariableValue newQualifier) {
|
||||
return myFactory.getVarFactory().createVariableValue(mySource, myVarType, myIsNegated, newQualifier);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"HardCodedStringLiteral"})
|
||||
public String toString() {
|
||||
return (myIsNegated ? "!" : "") + (myQualifier == null ? "" : myQualifier + ".") + ((PsiNamedElement)myVariable).getName();
|
||||
return (myIsNegated ? "!" : "") + (myQualifier == null ? "" : myQualifier + ".") + mySource;
|
||||
}
|
||||
|
||||
private boolean hardEquals(PsiModifierListOwner psiVar, PsiType varType, boolean negated, DfaVariableValue qualifier) {
|
||||
return (psiVar == myVariable || SpecialField.ARRAY_LENGTH.isMyAccessor(psiVar) && SpecialField.ARRAY_LENGTH.isMyAccessor(myVariable)) &&
|
||||
private boolean hardEquals(DfaVariableSource source, PsiType varType, boolean negated, DfaVariableValue qualifier) {
|
||||
return source.equals(mySource) &&
|
||||
negated == myIsNegated &&
|
||||
qualifier == myQualifier &&
|
||||
Comparing.equal(TypeConversionUtil.erasure(varType), TypeConversionUtil.erasure(myVarType));
|
||||
@@ -157,20 +189,10 @@ public class DfaVariableValue extends DfaValue {
|
||||
}
|
||||
|
||||
public boolean isFlushableByCalls() {
|
||||
if (myVariable instanceof PsiLocalVariable || myVariable instanceof PsiParameter || ControlFlowAnalyzer.isTempVariable(myVariable)) {
|
||||
return false;
|
||||
}
|
||||
boolean finalField = myVariable instanceof PsiVariable && myVariable.hasModifierProperty(PsiModifier.FINAL);
|
||||
boolean specialFinalField = myVariable instanceof PsiMethod &&
|
||||
Arrays.stream(SpecialField.values()).anyMatch(sf -> sf.isFinal() && sf.isMyAccessor(myVariable));
|
||||
if (finalField || specialFinalField) {
|
||||
return myQualifier != null && myQualifier.isFlushableByCalls();
|
||||
}
|
||||
return true;
|
||||
return !mySource.isStable() || (myQualifier != null && myQualifier.isFlushableByCalls());
|
||||
}
|
||||
|
||||
public boolean containsCalls() {
|
||||
return myVariable instanceof PsiMethod || myQualifier != null && myQualifier.containsCalls();
|
||||
return mySource.isCall() || myQualifier != null && myQualifier.containsCalls();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -130,8 +130,8 @@ public class SuspiciousComparatorCompareInspection extends BaseInspection {
|
||||
@Override
|
||||
protected DfaMemoryState createMemoryState() {
|
||||
DfaMemoryState state = super.createMemoryState();
|
||||
DfaVariableValue var1 = getFactory().getVarFactory().createVariableValue(parameters[0], false);
|
||||
DfaVariableValue var2 = getFactory().getVarFactory().createVariableValue(parameters[1], false);
|
||||
DfaVariableValue var1 = getFactory().getVarFactory().createVariableValue(parameters[0]);
|
||||
DfaVariableValue var2 = getFactory().getVarFactory().createVariableValue(parameters[1]);
|
||||
DfaValue condition = getFactory().createCondition(var1, DfaRelationValue.RelationType.EQ, var2);
|
||||
state.applyCondition(condition);
|
||||
return state;
|
||||
|
||||
+3
-4
@@ -127,9 +127,8 @@ public class CatchMayIgnoreExceptionInspection extends AbstractBaseJavaLocalInsp
|
||||
|
||||
DataFlowRunner runner = new StandardDataFlowRunner(false, block);
|
||||
DfaValueFactory factory = runner.getFactory();
|
||||
DfaVariableValue exceptionVar = factory.getVarFactory().createVariableValue(parameter, false);
|
||||
DfaVariableValue stableExceptionVar =
|
||||
factory.getVarFactory().createVariableValue(new LightParameter("tmp", exception, block), false);
|
||||
DfaVariableValue exceptionVar = factory.getVarFactory().createVariableValue(parameter);
|
||||
DfaVariableValue stableExceptionVar = factory.getVarFactory().createVariableValue(new LightParameter("tmp", exception, block));
|
||||
|
||||
StandardInstructionVisitor visitor = new IgnoredExceptionVisitor(parameter, block, exceptionClass, stableExceptionVar);
|
||||
Consumer<DfaMemoryState> stateAdjuster = state -> {
|
||||
@@ -178,7 +177,7 @@ public class CatchMayIgnoreExceptionInspection extends AbstractBaseJavaLocalInsp
|
||||
|
||||
protected boolean isModificationAllowed(DfaVariableValue variable) {
|
||||
PsiModifierListOwner owner = variable.getPsiVariable();
|
||||
return owner == myParameter || PsiTreeUtil.isAncestor(myBlock, owner, false);
|
||||
return owner == myParameter || owner != null && PsiTreeUtil.isAncestor(myBlock, owner, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user