From ce2a2e430551f41af06b0d78a8ae3adefddf0857 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 17 Dec 2018 15:36:15 +0700 Subject: [PATCH] GetFieldInstruction --- .../codeInspection/dataFlow/CFGBuilder.java | 20 ++++- .../dataFlow/ControlFlowAnalyzer.java | 36 ++++---- .../dataFlow/DfaMemoryStateImpl.java | 7 +- .../codeInspection/dataFlow/DfaUtil.java | 2 +- .../dataFlow/InstructionVisitor.java | 5 +- .../codeInspection/dataFlow/SpecialField.java | 26 +++--- .../dataFlow/SpecialFieldValue.java | 5 +- .../dataFlow/StandardInstructionVisitor.java | 6 +- .../dataFlow/inliner/StreamChainInliner.java | 53 +++++------ .../instructions/GetFieldInstruction.java | 57 ++++++++++++ .../instructions/UnboxingInstruction.java | 32 ------- .../dataFlow/value/DfaBoxedValue.java | 10 --- .../dataFlow/value/DfaExpressionFactory.java | 88 +++++++++---------- .../dataFlow/value/DfaVariableSource.java | 20 ++++- .../dataFlow/value/DfaVariableValue.java | 6 +- .../fixture/ArrayInitializerLength.java | 4 + .../fixture/ForEachOverEmptyCollection.java | 7 ++ .../dataFlow/fixture/StreamInlining.java | 7 ++ 18 files changed, 216 insertions(+), 175 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GetFieldInstruction.java delete mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/UnboxingInstruction.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CFGBuilder.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CFGBuilder.java index 5e1829a6dfb9..c6c00ee0d730 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CFGBuilder.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CFGBuilder.java @@ -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 + *

+ * Stack before: ... qualifier + *

+ * 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. *

diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index af43506e3ed5..d4dd979f6488 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -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(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index 0a68ed21aa2f..47b64814f350 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -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) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java index 68aa98a00949..68bc120428d7 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java index 88d68d299412..7f0bd28a6e7a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java @@ -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); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/SpecialField.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/SpecialField.java index fb00520ca8ba..b6d7c8d26263 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/SpecialField.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/SpecialField.java @@ -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); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/SpecialFieldValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/SpecialFieldValue.java index 2cec59fe58a3..6959782a006d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/SpecialFieldValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/SpecialFieldValue.java @@ -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; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index 19c5c1cf76f3..4597c1c666b8 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -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()); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/StreamChainInliner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/StreamChainInliner.java index bad3802a8e5b..267a334ecf54 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/StreamChainInliner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/StreamChainInliner.java @@ -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(); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GetFieldInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GetFieldInstruction.java new file mode 100644 index 000000000000..09269a4a2970 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GetFieldInstruction.java @@ -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; + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/UnboxingInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/UnboxingInstruction.java deleted file mode 100644 index d3456df2bf6c..000000000000 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/UnboxingInstruction.java +++ /dev/null @@ -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"; - } -} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaBoxedValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaBoxedValue.java index 2b7471906939..02e73eb9a62f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaBoxedValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaBoxedValue.java @@ -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); - } - } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java index 493782fe2919..2bb8217446e1 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java @@ -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); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableSource.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableSource.java index c45c73d035ec..1d1bd7a56c90 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableSource.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableSource.java @@ -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. *

* 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())); + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java index f9b7ea1b5779..e3ca043e3897 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java @@ -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 key = Pair.create(source.toString(), qualifier); for (DfaVariableValue aVar : myExistingVars.get(key)) { if (aVar.hardEquals(source, varType, qualifier)) return aVar; diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ArrayInitializerLength.java b/java/java-tests/testData/inspection/dataFlow/fixture/ArrayInitializerLength.java index 47791dd4168f..74c39a4fffda 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/ArrayInitializerLength.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ArrayInitializerLength.java @@ -28,6 +28,10 @@ class ArrayInitializerLength { for (int i : arr) { System.out.println("never"); } + int length = 0; + for (int i : new int[length]) { + System.out.println(i); + } } void testConditional() { diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ForEachOverEmptyCollection.java b/java/java-tests/testData/inspection/dataFlow/fixture/ForEachOverEmptyCollection.java index 952d4d25a249..2f3d609d57ba 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/ForEachOverEmptyCollection.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ForEachOverEmptyCollection.java @@ -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.get(max == null ? 0 : 1)); } } + + void testDirect() { + for (Object o : Collections.emptyList()) { + System.out.println(o); + } + } } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/StreamInlining.java b/java/java-tests/testData/inspection/dataFlow/fixture/StreamInlining.java index 8198e64e169c..94e27ea287f1 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/StreamInlining.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/StreamInlining.java @@ -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 (result == null) { + System.out.println("impossible"); + } + } }