mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
GetFieldInstruction
This commit is contained in:
@@ -18,10 +18,7 @@ package com.intellij.codeInspection.dataFlow;
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInspection.dataFlow.inliner.CallInliner;
|
||||
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.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -120,6 +117,21 @@ public class CFGBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate instructions to load a qualified field described by given source which qualifier is on the stack
|
||||
* <p>
|
||||
* Stack before: ... qualifier
|
||||
* <p>
|
||||
* Stack after: ... loaded_field
|
||||
*
|
||||
* @param source a {@link DfaVariableSource} which describes a field to load
|
||||
* @param type a loaded field type
|
||||
* @return this builder
|
||||
*/
|
||||
public CFGBuilder getField(@NotNull DfaVariableSource source, @Nullable PsiType type) {
|
||||
return add(new GetFieldInstruction(source, type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate instructions to push given variable value on stack for subsequent write.
|
||||
* <p>
|
||||
|
||||
+17
-19
@@ -523,25 +523,23 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
|
||||
if (iteratedValue != null) {
|
||||
iteratedValue.accept(this);
|
||||
addInstruction(new PopInstruction());
|
||||
DfaValue qualifier = myFactory.createValue(iteratedValue);
|
||||
|
||||
if (qualifier instanceof DfaVariableValue) {
|
||||
PsiType type = iteratedValue.getType();
|
||||
SpecialField length = null;
|
||||
if (type instanceof PsiArrayType) {
|
||||
length = SpecialField.ARRAY_LENGTH;
|
||||
}
|
||||
else if (InheritanceUtil.isInheritor(type, JAVA_UTIL_COLLECTION)) {
|
||||
length = SpecialField.COLLECTION_SIZE;
|
||||
}
|
||||
if (length != null) {
|
||||
addInstruction(new PushInstruction(length.createValue(myFactory, qualifier), null));
|
||||
addInstruction(new PushInstruction(myFactory.getInt(0), null));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, iteratedValue, PsiType.BOOLEAN));
|
||||
addInstruction(new ConditionalGotoInstruction(loopEndOffset, false, null));
|
||||
hasSizeCheck = true;
|
||||
}
|
||||
PsiType type = iteratedValue.getType();
|
||||
SpecialField length = null;
|
||||
if (type instanceof PsiArrayType) {
|
||||
length = SpecialField.ARRAY_LENGTH;
|
||||
}
|
||||
else if (InheritanceUtil.isInheritor(type, JAVA_UTIL_COLLECTION)) {
|
||||
length = SpecialField.COLLECTION_SIZE;
|
||||
}
|
||||
if (length != null) {
|
||||
addInstruction(new GetFieldInstruction(length, PsiType.INT));
|
||||
addInstruction(new PushInstruction(myFactory.getInt(0), null));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, iteratedValue, PsiType.BOOLEAN));
|
||||
addInstruction(new ConditionalGotoInstruction(loopEndOffset, false, null));
|
||||
hasSizeCheck = true;
|
||||
} else {
|
||||
addInstruction(new PopInstruction());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1495,7 +1493,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
|
||||
if (TypeConversionUtil.isPrimitiveAndNotNull(expectedType) && TypeConversionUtil.isPrimitiveWrapper(actualType)) {
|
||||
PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(actualType); // expectedType is not always precise unboxed type
|
||||
addInstruction(new UnboxingInstruction(unboxedType));
|
||||
addInstruction(new GetFieldInstruction(SpecialField.UNBOX, unboxedType));
|
||||
}
|
||||
else if (TypeConversionUtil.isPrimitiveAndNotNull(actualType) && TypeConversionUtil.isAssignableFromPrimitiveWrapper(expectedType)) {
|
||||
addConditionalRuntimeThrow();
|
||||
|
||||
+3
-4
@@ -598,7 +598,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
if (value instanceof DfaVariableValue) {
|
||||
PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(value.getType());
|
||||
if (unboxedType != null) {
|
||||
value = myFactory.getBoxedFactory().createUnboxed(value, unboxedType);
|
||||
value = SpecialField.UNBOX.createValue(myFactory, value, unboxedType);
|
||||
}
|
||||
EqClass ec = getEqClass(value);
|
||||
return ec == null ? null : ec.findConstant();
|
||||
@@ -948,9 +948,8 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
return true;
|
||||
}
|
||||
|
||||
DfaBoxedValue.Factory boxedFactory = myFactory.getBoxedFactory();
|
||||
DfaValue unboxedLeft = boxedFactory.createUnboxed(dfaLeft, null);
|
||||
DfaValue unboxedRight = boxedFactory.createUnboxed(dfaRight, null);
|
||||
DfaValue unboxedLeft = SpecialField.UNBOX.createValue(myFactory, dfaLeft);
|
||||
DfaValue unboxedRight = SpecialField.UNBOX.createValue(myFactory, dfaRight);
|
||||
DfaConstValue leftConst = getConstantValue(unboxedLeft);
|
||||
DfaConstValue rightConst = getConstantValue(unboxedRight);
|
||||
if (leftConst != null && rightConst != null) {
|
||||
|
||||
@@ -354,7 +354,7 @@ public class DfaUtil {
|
||||
if (TypeConversionUtil.isPrimitiveAndNotNull(type)) {
|
||||
if (value instanceof DfaBoxedValue ||
|
||||
(value instanceof DfaVariableValue && TypeConversionUtil.isPrimitiveWrapper(value.getType()))) {
|
||||
return value.getFactory().getBoxedFactory().createUnboxed(value, ObjectUtils.tryCast(type, PsiPrimitiveType.class));
|
||||
return SpecialField.UNBOX.createValue(value.getFactory(), value, type);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
||||
+3
-2
@@ -159,9 +159,10 @@ public abstract class InstructionVisitor {
|
||||
return nextInstruction(instruction, runner, state);
|
||||
}
|
||||
|
||||
public DfaInstructionState[] visitUnbox(UnboxingInstruction instruction, DataFlowRunner runner, DfaMemoryState state) {
|
||||
public DfaInstructionState[] visitGetField(GetFieldInstruction instruction, DataFlowRunner runner, DfaMemoryState state) {
|
||||
DfaValue value = state.pop();
|
||||
state.push(runner.getFactory().getBoxedFactory().createUnboxed(value, instruction.getTargetType()));
|
||||
DfaValue field = instruction.getSource().createValue(runner.getFactory(), value, instruction.getTargetType());
|
||||
pushExpressionResult(field, instruction, state);
|
||||
return nextInstruction(instruction, runner, state);
|
||||
}
|
||||
|
||||
|
||||
+15
-11
@@ -94,6 +94,15 @@ public enum SpecialField implements DfaVariableSource {
|
||||
return DfaUnknownValue.getInstance();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DfaValue createValue(@NotNull DfaValueFactory factory, @Nullable DfaValue qualifier, PsiType targetType) {
|
||||
if (qualifier instanceof DfaBoxedValue) {
|
||||
return ((DfaBoxedValue)qualifier).getWrappedValue();
|
||||
}
|
||||
return super.createValue(factory, qualifier, targetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isMyQualifierType(PsiType type) {
|
||||
return TypeConversionUtil.isPrimitiveWrapper(type);
|
||||
@@ -157,19 +166,14 @@ public enum SpecialField implements DfaVariableSource {
|
||||
* @param qualifier a known qualifier value
|
||||
* @return a DfaValue which represents this special field
|
||||
*/
|
||||
public DfaValue createValue(DfaValueFactory factory, DfaValue qualifier) {
|
||||
@NotNull
|
||||
public final DfaValue createValue(@NotNull DfaValueFactory factory, @Nullable DfaValue qualifier) {
|
||||
return createValue(factory, qualifier, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a DfaValue which represents this special field
|
||||
*
|
||||
* @param factory a factory to create new values if necessary
|
||||
* @param qualifier a known qualifier value
|
||||
* @param targetType a type of created value
|
||||
* @return a DfaValue which represents this special field
|
||||
*/
|
||||
public DfaValue createValue(DfaValueFactory factory, DfaValue qualifier, PsiType targetType) {
|
||||
@NotNull
|
||||
@Override
|
||||
public DfaValue createValue(@NotNull DfaValueFactory factory, @Nullable DfaValue qualifier, PsiType targetType) {
|
||||
if (qualifier instanceof DfaVariableValue) {
|
||||
DfaVariableValue variableValue = (DfaVariableValue)qualifier;
|
||||
PsiModifierListOwner psiVariable = variableValue.getPsiVariable();
|
||||
@@ -185,7 +189,7 @@ public enum SpecialField implements DfaVariableSource {
|
||||
}
|
||||
}
|
||||
}
|
||||
return factory.getVarFactory().createVariableValue(this, targetType == null ? getType(variableValue) : targetType, variableValue);
|
||||
return DfaVariableSource.super.createValue(factory, qualifier, targetType == null ? getType(variableValue) : targetType);
|
||||
}
|
||||
if(qualifier instanceof DfaFactMapValue) {
|
||||
SpecialFieldValue sfValue = ((DfaFactMapValue)qualifier).get(DfaFactType.SPECIAL_FIELD_VALUE);
|
||||
|
||||
+2
-3
@@ -5,7 +5,6 @@ import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaFactMapValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -14,7 +13,7 @@ import java.util.Objects;
|
||||
*/
|
||||
public final class SpecialFieldValue {
|
||||
private final @NotNull SpecialField myField;
|
||||
private final @Nullable DfaValue myValue;
|
||||
private final @NotNull DfaValue myValue;
|
||||
|
||||
public SpecialFieldValue(@NotNull SpecialField field, @NotNull DfaValue value) {
|
||||
if (value instanceof DfaFactMapValue) {
|
||||
@@ -34,7 +33,7 @@ public final class SpecialFieldValue {
|
||||
return myField;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
public DfaValue getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
+1
-5
@@ -512,13 +512,9 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
|
||||
if (instruction.getContext() instanceof PsiMethodReferenceExpression && qualifierValue instanceof DfaVariableValue) {
|
||||
PsiMethod method = instruction.getTargetMethod();
|
||||
SpecialField field = SpecialField.findSpecialField(method);
|
||||
if (field != null) {
|
||||
return field.createValue(factory, qualifierValue);
|
||||
}
|
||||
DfaVariableSource source = DfaExpressionFactory.getAccessedVariableOrGetter(method);
|
||||
if (source != null) {
|
||||
return factory.getVarFactory().createVariableValue(source, instruction.getResultType(), (DfaVariableValue)qualifierValue);
|
||||
return source.createValue(factory, qualifierValue, instruction.getResultType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-32
@@ -725,42 +725,31 @@ public class StreamChainInliner implements CallInliner {
|
||||
.chain(firstStep::iteration);
|
||||
return;
|
||||
}
|
||||
PsiExpression qualifierExpression = null;
|
||||
SpecialField sizeField = null;
|
||||
if (array) {
|
||||
PsiExpression qualifierExpression = sourceCall.getArgumentList().getExpressions()[0];
|
||||
DfaValue qualifierValue = builder.getFactory().createValue(qualifierExpression);
|
||||
if (qualifierValue != null) {
|
||||
builder.pushExpression(qualifierExpression)
|
||||
.chain(firstStep::before)
|
||||
.pop()
|
||||
.push(SpecialField.ARRAY_LENGTH.createValue(builder.getFactory(), qualifierValue))
|
||||
.push(builder.getFactory().getInt(0))
|
||||
.ifCondition(JavaTokenType.GT)
|
||||
.chain(b -> makeMainLoop(b, firstStep, inType))
|
||||
.end();
|
||||
return;
|
||||
}
|
||||
qualifierExpression = sourceCall.getArgumentList().getExpressions()[0];
|
||||
sizeField = SpecialField.ARRAY_LENGTH;
|
||||
}
|
||||
if (COLLECTION_STREAM.test(sourceCall)) {
|
||||
PsiExpression qualifierExpression = sourceCall.getMethodExpression().getQualifierExpression();
|
||||
DfaValue qualifierValue = builder.getFactory().createValue(qualifierExpression);
|
||||
if (qualifierValue != null) {
|
||||
builder.pushExpression(qualifierExpression)
|
||||
.chain(firstStep::before)
|
||||
.pop()
|
||||
.push(SpecialField.COLLECTION_SIZE.createValue(builder.getFactory(), qualifierValue))
|
||||
.push(builder.getFactory().getInt(0))
|
||||
.ifCondition(JavaTokenType.GT)
|
||||
.chain(b -> makeMainLoop(b, firstStep, inType))
|
||||
.end();
|
||||
return;
|
||||
}
|
||||
else if (COLLECTION_STREAM.test(sourceCall)) {
|
||||
qualifierExpression = sourceCall.getMethodExpression().getQualifierExpression();
|
||||
sizeField = SpecialField.COLLECTION_SIZE;
|
||||
}
|
||||
if (qualifierExpression != null) {
|
||||
builder.pushExpression(qualifierExpression)
|
||||
.chain(firstStep::before)
|
||||
.getField(sizeField, PsiType.INT)
|
||||
.push(builder.getFactory().getInt(0))
|
||||
.ifCondition(JavaTokenType.GT);
|
||||
} else {
|
||||
builder
|
||||
.pushExpression(originalQualifier)
|
||||
.pop()
|
||||
.chain(firstStep::before)
|
||||
.pushUnknown()
|
||||
.ifConditionIs(true);
|
||||
}
|
||||
builder
|
||||
.pushExpression(originalQualifier)
|
||||
.pop()
|
||||
.chain(firstStep::before)
|
||||
.pushUnknown()
|
||||
.ifConditionIs(true)
|
||||
.chain(b -> makeMainLoop(b, firstStep, inType))
|
||||
.end();
|
||||
}
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// 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.instructions;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowRunner;
|
||||
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
|
||||
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableSource;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.psi.PsiType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Instruction to push a field qualified by the value on the stack
|
||||
*/
|
||||
public class GetFieldInstruction extends Instruction implements ExpressionPushingInstruction {
|
||||
@Nullable private final PsiType myTargetType;
|
||||
@NotNull private final DfaVariableSource mySource;
|
||||
@Nullable private final PsiExpression myAnchor;
|
||||
|
||||
public GetFieldInstruction(@NotNull DfaVariableSource source, @Nullable PsiType targetType) {
|
||||
this(null, source, targetType);
|
||||
}
|
||||
|
||||
public GetFieldInstruction(@Nullable PsiExpression anchor, @NotNull DfaVariableSource source, @Nullable PsiType targetType) {
|
||||
myTargetType = targetType;
|
||||
mySource = source;
|
||||
myAnchor = anchor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
|
||||
return visitor.visitGetField(this, runner, stateBefore);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaVariableSource getSource() {
|
||||
return mySource;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiType getTargetType() {
|
||||
return myTargetType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GET_FIELD " + mySource;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiExpression getExpression() {
|
||||
return myAnchor;
|
||||
}
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
// 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.instructions;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowRunner;
|
||||
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
|
||||
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class UnboxingInstruction extends Instruction {
|
||||
@Nullable private final PsiPrimitiveType myTargetType;
|
||||
|
||||
public UnboxingInstruction(@Nullable PsiPrimitiveType targetType) {
|
||||
myTargetType = targetType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
|
||||
return visitor.visitUnbox(this, runner, stateBefore);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiPrimitiveType getTargetType() {
|
||||
return myTargetType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UNBOX";
|
||||
}
|
||||
}
|
||||
-10
@@ -16,7 +16,6 @@
|
||||
package com.intellij.codeInspection.dataFlow.value;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.*;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -84,14 +83,5 @@ public class DfaBoxedValue extends DfaValue {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaValue createUnboxed(DfaValue value, PsiPrimitiveType targetType) {
|
||||
if (value instanceof DfaBoxedValue) {
|
||||
return ((DfaBoxedValue)value).getWrappedValue();
|
||||
}
|
||||
return SpecialField.UNBOX.createValue(myFactory, value, targetType);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+40
-48
@@ -139,43 +139,21 @@ public class DfaExpressionFactory {
|
||||
}
|
||||
|
||||
private DfaValue createReferenceValue(@NotNull PsiReferenceExpression refExpr) {
|
||||
DfaValue specialValue = createFromSpecialField(refExpr);
|
||||
if (specialValue != null) {
|
||||
return specialValue;
|
||||
PsiElement target = refExpr.resolve();
|
||||
if (target instanceof PsiVariable) {
|
||||
PsiVariable variable = (PsiVariable)target;
|
||||
if (variable.hasModifierProperty(PsiModifier.FINAL) && !PsiUtil.isAccessedForWriting(refExpr)) {
|
||||
DfaValue constValue = myFactory.getConstFactory().create(variable);
|
||||
if (constValue != null && !maybeUninitializedConstant(constValue, refExpr, variable)) return constValue;
|
||||
}
|
||||
}
|
||||
DfaVariableSource var = getAccessedVariableOrGetter(refExpr.resolve());
|
||||
DfaVariableSource var = getAccessedVariableOrGetter(target);
|
||||
if (var == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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 && ((PsiVariable)psiElement).getType().equalsToText(CommonClassNames.JAVA_LANG_VOID)) {
|
||||
return myFactory.getConstFactory().getNull();
|
||||
}
|
||||
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 instanceof PsiLocalVariable || psiElement instanceof PsiParameter ||
|
||||
(psiElement instanceof PsiField &&
|
||||
psiElement.hasModifierProperty(PsiModifier.STATIC) &&
|
||||
!psiElement.hasModifierProperty(PsiModifier.FINAL)) ||
|
||||
isStaticFinalConstantWithoutInitializationHacks(psiElement) ||
|
||||
(psiElement instanceof PsiMethod && psiElement.hasModifierProperty(PsiModifier.STATIC))) {
|
||||
return myFactory.getVarFactory().createVariableValue(var, refExpr.getType());
|
||||
}
|
||||
DfaVariableValue qualifier = getQualifierOrThisVariable(refExpr);
|
||||
PsiType type = refExpr.getType();
|
||||
if (qualifier != null) {
|
||||
return myFactory.getVarFactory().createVariableValue(var, type, qualifier);
|
||||
}
|
||||
|
||||
return myFactory.createTypeValue(type, DfaPsiUtil.getElementNullability(type, psiElement));
|
||||
return var.createValue(myFactory, qualifier, refExpr.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,25 +214,13 @@ public class DfaExpressionFactory {
|
||||
return PsiTreeUtil.getTopmostParentOfType(refExpr, PsiClass.class) == PsiTreeUtil.getTopmostParentOfType(var, PsiClass.class);
|
||||
}
|
||||
|
||||
private static boolean isStaticFinalConstantWithoutInitializationHacks(PsiModifierListOwner var) {
|
||||
return (var instanceof PsiField && var.hasModifierProperty(PsiModifier.FINAL) && var.hasModifierProperty(PsiModifier.STATIC)) &&
|
||||
!DfaUtil.hasInitializationHacks((PsiField)var);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DfaValue createFromSpecialField(PsiReferenceExpression refExpr) {
|
||||
PsiElement target = refExpr.resolve();
|
||||
if (!(target instanceof PsiModifierListOwner)) return null;
|
||||
SpecialField sf = SpecialField.findSpecialField(target);
|
||||
if (sf == null) return null;
|
||||
DfaVariableValue qualifier = getQualifierOrThisVariable(refExpr);
|
||||
if (qualifier == null) return null;
|
||||
return sf.createValue(myFactory, qualifier);
|
||||
}
|
||||
|
||||
@Contract("null -> null")
|
||||
@Nullable
|
||||
public static DfaVariableSource getAccessedVariableOrGetter(final PsiElement target) {
|
||||
SpecialField sf = SpecialField.findSpecialField(target);
|
||||
if (sf != null) {
|
||||
return sf;
|
||||
}
|
||||
if (target instanceof PsiVariable) {
|
||||
return new PlainSource((PsiVariable)target);
|
||||
}
|
||||
@@ -351,7 +317,7 @@ public class DfaExpressionFactory {
|
||||
}
|
||||
ArrayElementSource indexVariable = getArrayIndexVariable(index);
|
||||
if (indexVariable == null) return null;
|
||||
return myFactory.getVarFactory().createVariableValue(indexVariable, componentType, arrayDfaVar);
|
||||
return indexVariable.createValue(myFactory, arrayDfaVar, componentType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -387,6 +353,23 @@ public class DfaExpressionFactory {
|
||||
myVariable.hasModifierProperty(PsiModifier.FINAL);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DfaValue createValue(@NotNull DfaValueFactory factory, @Nullable DfaValue qualifier, @Nullable PsiType type) {
|
||||
if (myVariable.getType().equalsToText(CommonClassNames.JAVA_LANG_VOID)) {
|
||||
return factory.getConstFactory().getNull();
|
||||
}
|
||||
if (myVariable.hasModifierProperty(PsiModifier.VOLATILE)) {
|
||||
return factory.createTypeValue(type, DfaPsiUtil.getElementNullability(type, myVariable));
|
||||
}
|
||||
if (myVariable instanceof PsiLocalVariable || myVariable instanceof PsiParameter ||
|
||||
(myVariable instanceof PsiField && myVariable.hasModifierProperty(PsiModifier.STATIC) &&
|
||||
(!myVariable.hasModifierProperty(PsiModifier.FINAL) || !DfaUtil.hasInitializationHacks((PsiField)myVariable)))) {
|
||||
return factory.getVarFactory().createVariableValue(this, type);
|
||||
}
|
||||
return DfaVariableSource.super.createValue(factory, qualifier, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj == this || obj instanceof PlainSource && ((PlainSource)obj).myVariable == myVariable;
|
||||
@@ -422,6 +405,15 @@ public class DfaExpressionFactory {
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DfaValue createValue(@NotNull DfaValueFactory factory, @Nullable DfaValue qualifier, @Nullable PsiType type) {
|
||||
if (myGetter.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
return factory.getVarFactory().createVariableValue(this, type);
|
||||
}
|
||||
return DfaVariableSource.super.createValue(factory, qualifier, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj == this || (obj instanceof GetterSource && ((GetterSource)obj).myGetter == myGetter);
|
||||
|
||||
+19
-1
@@ -1,11 +1,14 @@
|
||||
// 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.codeInspection.dataFlow.DfaPsiUtil;
|
||||
import com.intellij.psi.PsiModifierListOwner;
|
||||
import com.intellij.psi.PsiType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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.
|
||||
* Represents a source of {@link DfaVariableValue}. Two variables are the same if they have the same source and qualifier.
|
||||
* 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
|
||||
@@ -38,4 +41,19 @@ public interface DfaVariableSource {
|
||||
*/
|
||||
@Override
|
||||
String toString();
|
||||
|
||||
/**
|
||||
* Returns a value which describes the field qualified by given qualifier and described by this source
|
||||
* @param factory factory to use
|
||||
* @param qualifier qualifier to use
|
||||
* @param type field type
|
||||
* @return a field value
|
||||
*/
|
||||
@NotNull
|
||||
default DfaValue createValue(@NotNull DfaValueFactory factory, @Nullable DfaValue qualifier, @Nullable PsiType type) {
|
||||
if (qualifier instanceof DfaVariableValue) {
|
||||
return factory.getVarFactory().createVariableValue(this, type, (DfaVariableValue)qualifier);
|
||||
}
|
||||
return factory.createTypeValue(type, DfaPsiUtil.getElementNullability(type, getPsiElement()));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -74,9 +74,9 @@ public final class DfaVariableValue extends DfaValue {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaVariableValue createVariableValue(@NotNull DfaVariableSource source,
|
||||
@Nullable PsiType varType,
|
||||
@Nullable DfaVariableValue qualifier) {
|
||||
DfaVariableValue createVariableValue(@NotNull DfaVariableSource source,
|
||||
@Nullable PsiType varType,
|
||||
@Nullable DfaVariableValue qualifier) {
|
||||
Pair<String, DfaVariableValue> key = Pair.create(source.toString(), qualifier);
|
||||
for (DfaVariableValue aVar : myExistingVars.get(key)) {
|
||||
if (aVar.hardEquals(source, varType, qualifier)) return aVar;
|
||||
|
||||
@@ -28,6 +28,10 @@ class ArrayInitializerLength {
|
||||
for (int i : <warning descr="Array 'arr' is always empty">arr</warning>) {
|
||||
System.out.println("never");
|
||||
}
|
||||
int length = 0;
|
||||
for (int i : <warning descr="Array 'new int[length]' is always empty">new int[length]</warning>) {
|
||||
System.out.println(i);
|
||||
}
|
||||
}
|
||||
|
||||
void testConditional() {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ForEachOverEmptyCollection {
|
||||
@@ -56,4 +57,10 @@ public class ForEachOverEmptyCollection {
|
||||
list.<warning descr="The call to 'get' always fails as index is out of bounds">get</warning>(<warning descr="Condition 'max == null' is always 'true'">max == null</warning> ? 0 : 1));
|
||||
}
|
||||
}
|
||||
|
||||
void testDirect() {
|
||||
for (Object o : <warning descr="Collection 'Collections.emptyList()' is always empty">Collections.emptyList()</warning>) {
|
||||
System.out.println(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,4 +257,11 @@ public class StreamInlining {
|
||||
System.out.println("found");
|
||||
}
|
||||
}
|
||||
|
||||
void testImmediateCollection() {
|
||||
String result = Collections.singleton(" foo ").stream().map(String::trim).findFirst().orElse(null);
|
||||
if (<warning descr="Condition 'result == null' is always 'false'">result == null</warning>) {
|
||||
System.out.println("impossible");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user