From 458718320987cffc6174d0f700b1fb436d52b772 Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 3 Feb 2013 20:26:41 +0100 Subject: [PATCH] single dfa variable nullability --- .../dataFlow/DfaMemoryStateImpl.java | 15 ++++---- .../codeInspection/dataFlow/DfaUtil.java | 26 ++++++------- .../dataFlow/DfaVariableState.java | 26 ++++++------- .../codeInspection/dataFlow/Nullness.java | 23 ++++++++++++ .../dataFlow/StandardInstructionVisitor.java | 25 ++++++------- .../dataFlow/value/DfaValueFactory.java | 6 +-- .../dataFlow/value/DfaVariableValue.java | 21 ++++++----- .../slicer/SliceNullnessAnalyzer.java | 37 ++++++++++--------- 8 files changed, 101 insertions(+), 78 deletions(-) create mode 100644 java/java-impl/src/com/intellij/codeInspection/dataFlow/Nullness.java diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index 5248baeaab47..87e0d2dad351 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -28,7 +28,9 @@ import com.intellij.codeInspection.dataFlow.value.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.PsiPrimitiveType; +import com.intellij.psi.PsiType; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; @@ -769,18 +771,15 @@ public class DfaMemoryStateImpl implements DfaMemoryState { public void flushFields(DataFlowRunner runner) { for (DfaVariableValue field : runner.getFields()) { if (myVariableStates.containsKey(field) || getEqClassIndex(field) >= 0) { - flushVariable(field); - getVariableState(field).setNullable(false); + if (!DfaUtil.isFinalField(field.getPsiVariable())) { + flushWithDependencies(field); + getVariableState(field).setNullable(false); + } } } } public void flushVariable(@NotNull DfaVariableValue variable) { - PsiVariable psiVariable = variable.getPsiVariable(); - if (DfaUtil.isFinalField(psiVariable)) { - return; - } - flushWithDependencies(variable); } diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java index b494b536b5d2..69c7b05f8e2b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java @@ -86,17 +86,17 @@ public class DfaUtil { return expressions == null ? Collections.emptyList() : expressions; } - @Nullable - public static Boolean getElementNullability(@Nullable PsiType resultType, @Nullable PsiModifierListOwner owner) { + @NotNull + public static Nullness getElementNullability(@Nullable PsiType resultType, @Nullable PsiModifierListOwner owner) { if (owner == null) { - return null; + return Nullness.UNKNOWN; } if (NullableNotNullManager.isNullable(owner)) { - return Boolean.TRUE; + return Nullness.NULLABLE; } if (NullableNotNullManager.isNotNull(owner)) { - return Boolean.FALSE; + return Nullness.NOT_NULL; } if (resultType != null) { @@ -104,15 +104,15 @@ public class DfaUtil { for (PsiAnnotation annotation : resultType.getAnnotations()) { String qualifiedName = annotation.getQualifiedName(); if (nnn.getNullables().contains(qualifiedName)) { - return Boolean.TRUE; + return Nullness.NULLABLE; } if (nnn.getNotNulls().contains(qualifiedName)) { - return Boolean.FALSE; + return Nullness.NOT_NULL; } } } - return null; + return Nullness.UNKNOWN; } public static boolean isNullableInitialized(PsiVariable var, boolean nullable) { @@ -143,12 +143,12 @@ public class DfaUtil { return !nullable; } - public static boolean isFinalField(PsiVariable var) { - return var.hasModifierProperty(PsiModifier.FINAL) && !var.hasModifierProperty(PsiModifier.TRANSIENT) && var instanceof PsiField; + public static boolean isPlainMutableField(PsiVariable var) { + return !var.hasModifierProperty(PsiModifier.FINAL) && !var.hasModifierProperty(PsiModifier.TRANSIENT) && !var.hasModifierProperty(PsiModifier.VOLATILE) && var instanceof PsiField; } - public static enum Nullness { - NOT_NULL,NULL,UNKNOWN + public static boolean isFinalField(PsiVariable var) { + return var.hasModifierProperty(PsiModifier.FINAL) && !var.hasModifierProperty(PsiModifier.TRANSIENT) && var instanceof PsiField; } @NotNull @@ -164,7 +164,7 @@ public class DfaUtil { if (result != RunnerResult.OK) { return Nullness.UNKNOWN; } - if (visitor.myNulls.contains(variable) && !visitor.myNotNulls.contains(variable)) return Nullness.NULL; + if (visitor.myNulls.contains(variable) && !visitor.myNotNulls.contains(variable)) return Nullness.NULLABLE; if (visitor.myNotNulls.contains(variable) && !visitor.myNulls.contains(variable)) return Nullness.NOT_NULL; return Nullness.UNKNOWN; } diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java index c457d6e539b6..7c86f7f4e27b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java @@ -40,27 +40,23 @@ import java.util.Set; public class DfaVariableState implements Cloneable { private final Set myInstanceofValues; private final Set myNotInstanceofValues; - private boolean myNullable = false; - private final boolean myVariableIsDeclaredNotNull; + private Nullness myNullability; public DfaVariableState(@NotNull DfaVariableValue dfaVar) { myInstanceofValues = new HashSet(); myNotInstanceofValues = new HashSet(); - Boolean nullability = dfaVar.getInherentNullability(); - myNullable = nullability == Boolean.TRUE; - myVariableIsDeclaredNotNull = nullability == Boolean.FALSE; + myNullability = dfaVar.getInherentNullability(); } protected DfaVariableState(final DfaVariableState toClone) { myInstanceofValues = new THashSet(toClone.myInstanceofValues); myNotInstanceofValues = new THashSet(toClone.myNotInstanceofValues); - myNullable = toClone.myNullable; - myVariableIsDeclaredNotNull = toClone.myVariableIsDeclaredNotNull; + myNullability = toClone.myNullability; } public boolean isNullable() { - return myNullable; + return myNullability == Nullness.NULLABLE; } private boolean checkInstanceofValue(DfaTypeValue dfaType) { @@ -78,7 +74,9 @@ public class DfaVariableState implements Cloneable { } public boolean setInstanceofValue(DfaTypeValue dfaType) { - myNullable |= dfaType.isNullable(); + if (dfaType.isNullable()) { + myNullability = Nullness.NULLABLE; + } if (dfaType.getType() instanceof PsiPrimitiveType) return true; @@ -111,7 +109,7 @@ public class DfaVariableState implements Cloneable { DfaVariableState aState = (DfaVariableState) obj; return myInstanceofValues.equals(aState.myInstanceofValues) && myNotInstanceofValues.equals(aState.myNotInstanceofValues) && - myNullable == aState.myNullable; + myNullability == aState.myNullability; } protected DfaVariableState clone() { @@ -134,16 +132,18 @@ public class DfaVariableState implements Cloneable { buf.append("{").append(dfaTypeValue).append("}"); if (iterator.hasNext()) buf.append(", "); } - buf.append(", nullable=").append(myNullable); + buf.append(", nullable=").append(myNullability); return buf.toString(); } public boolean isNotNull() { - return myVariableIsDeclaredNotNull; + return myNullability == Nullness.NOT_NULL; } public void setNullable(final boolean nullable) { - myNullable = nullable; + if (myNullability != Nullness.NOT_NULL) { + myNullability = nullable ? Nullness.NULLABLE : Nullness.UNKNOWN; + } } public void setValue(DfaValue value) { diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/Nullness.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/Nullness.java new file mode 100644 index 000000000000..3dd099b0c1d3 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/Nullness.java @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.dataFlow; + +/** +* @author cdr +*/ +public enum Nullness { + NOT_NULL, NULLABLE,UNKNOWN +} diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index a216be6f3cb0..54df852bfbab 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -40,26 +40,26 @@ public class StandardInstructionVisitor extends InstructionVisitor { private final Set myCanBeNullInInstanceof = new THashSet(); private final Set myNotToReportReachability = new THashSet(); private final Set myUsefulInstanceofs = new THashSet(); - private final FactoryMap> myParametersNullability = new FactoryMap>() { + private final FactoryMap> myParametersNullability = new FactoryMap>() { @Nullable @Override - protected Map create(MethodCallInstruction key) { + protected Map create(MethodCallInstruction key) { return calcParameterNullability(key.getCallExpression()); } }; - private final FactoryMap myReturnTypeNullability = new FactoryMap() { + private final FactoryMap myReturnTypeNullability = new FactoryMap() { @Override - protected Boolean create(MethodCallInstruction key) { + protected Nullness create(MethodCallInstruction key) { final PsiCallExpression callExpression = key.getCallExpression(); if (callExpression instanceof PsiNewExpression) { - return Boolean.FALSE; + return Nullness.NOT_NULL; } return callExpression != null ? DfaUtil.getElementNullability(key.getResultType(), callExpression.resolveMethod()) : null; } }; - private static Map calcParameterNullability(@Nullable PsiCallExpression callExpression) { + private static Map calcParameterNullability(@Nullable PsiCallExpression callExpression) { PsiExpressionList argumentList = callExpression == null ? null : callExpression.getArgumentList(); if (argumentList != null) { JavaResolveResult result = callExpression.resolveMethodGenerics(); @@ -72,7 +72,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { boolean varArg = isVarArgCall(method, substitutor, args, parameters); int checkedCount = Math.min(args.length, parameters.length) - (varArg ? 1 : 0); - Map map = ContainerUtil.newHashMap(); + Map map = ContainerUtil.newHashMap(); for (int i = 0; i < checkedCount; i++) { map.put(args[i], DfaUtil.getElementNullability(substitutor.substitute(parameters[i].getType()), parameters[i])); } @@ -112,7 +112,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { if (dfaDest instanceof DfaVariableValue) { DfaVariableValue var = (DfaVariableValue) dfaDest; final PsiVariable psiVariable = var.getPsiVariable(); - if (DfaUtil.getElementNullability(var.getVariableType(), psiVariable) == Boolean.FALSE) { + if (DfaUtil.getElementNullability(var.getVariableType(), psiVariable) == Nullness.NOT_NULL) { if (!memState.applyNotNull(dfaSource)) { onAssigningToNotNullableVariable(instruction); } @@ -183,12 +183,12 @@ public class StandardInstructionVisitor extends InstructionVisitor { @Override public DfaInstructionState[] visitMethodCall(MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { final PsiExpression[] args = instruction.getArgs(); - Map map = myParametersNullability.get(instruction); + Map map = myParametersNullability.get(instruction); final DfaNotNullValue.Factory factory = runner.getFactory().getNotNullFactory(); for (int i = 0; i < args.length; i++) { final DfaValue arg = memState.pop(); PsiExpression expr = args[(args.length - i - 1)]; - if (map.get(expr) == Boolean.FALSE) { + if (map.get(expr) == Nullness.NOT_NULL) { if (!memState.applyNotNull(arg)) { onPassingNullParameter(expr); if (arg instanceof DfaVariableValue) { @@ -196,7 +196,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { } } } - else if (map.containsKey(expr) && map.get(expr) == null && !memState.checkNotNullable(arg)) { + else if (map.get(expr) == Nullness.UNKNOWN && !memState.checkNotNullable(arg)) { onPassingNullParameterToNonAnnotated(runner, expr); } } @@ -230,8 +230,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { final PsiType type = instruction.getResultType(); final MethodCallInstruction.MethodType methodType = instruction.getMethodType(); if (type != null && (type instanceof PsiClassType || type.getArrayDimensions() > 0)) { - @Nullable final Boolean nullability = myReturnTypeNullability.get(instruction); - return factory.createTypeValueWithNullability(type, nullability); + return factory.createTypeValueWithNullability(type, myReturnTypeNullability.get(instruction)); } if (methodType == MethodCallInstruction.MethodType.UNBOXING) { diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java index 11627a7d0e84..77588e74e49e 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java @@ -24,11 +24,11 @@ */ package com.intellij.codeInspection.dataFlow.value; +import com.intellij.codeInspection.dataFlow.Nullness; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; import com.intellij.psi.impl.JavaConstantExpressionEvaluator; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,8 +51,8 @@ public class DfaValueFactory { myRelationFactory = new DfaRelationValue.Factory(this); } - public DfaValue createTypeValueWithNullability(@Nullable PsiType type, @Nullable Boolean nullability) { - return nullability == Boolean.FALSE ? getNotNullFactory().create(type) : getTypeFactory().create(type, nullability == Boolean.TRUE); + public DfaValue createTypeValueWithNullability(@Nullable PsiType type, Nullness nullability) { + return nullability == Nullness.NOT_NULL ? getNotNullFactory().create(type) : getTypeFactory().create(type, nullability == Nullness.NULLABLE); } int createID() { diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java index c4294e05fdb1..8f92ddf332b2 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java @@ -25,8 +25,9 @@ package com.intellij.codeInspection.dataFlow.value; import com.intellij.codeInspection.dataFlow.DfaUtil; -import com.intellij.openapi.util.Ref; -import com.intellij.psi.*; +import com.intellij.codeInspection.dataFlow.Nullness; +import com.intellij.psi.PsiType; +import com.intellij.psi.PsiVariable; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.Nullable; @@ -93,7 +94,7 @@ public class DfaVariableValue extends DfaValue { @Nullable private DfaVariableValue myQualifier; private boolean myIsNegated; private boolean myViaMethods; - private Ref myInherentNullability; + private Nullness myInherentNullability; private DfaVariableValue(PsiVariable variable, PsiType varType, boolean isNegated, DfaValueFactory factory, @Nullable DfaVariableValue qualifier, boolean viaMethods) { super(factory); @@ -150,22 +151,22 @@ public class DfaVariableValue extends DfaValue { return myViaMethods; } - public Boolean getInherentNullability() { + public Nullness getInherentNullability() { if (myInherentNullability != null) { - return myInherentNullability.get(); + return myInherentNullability; } PsiVariable var = getPsiVariable(); - Boolean nullability = DfaUtil.getElementNullability(getVariableType(), var); - if (nullability == null && var != null) { + Nullness nullability = DfaUtil.getElementNullability(getVariableType(), var); + if (nullability == Nullness.UNKNOWN && var != null) { if (DfaUtil.isNullableInitialized(var, true)) { - nullability = true; + nullability = Nullness.NULLABLE; } else if (DfaUtil.isNullableInitialized(var, false)) { - nullability = false; + nullability = Nullness.NOT_NULL; } } - myInherentNullability = Ref.create(nullability); + myInherentNullability = nullability; return nullability; } diff --git a/java/java-impl/src/com/intellij/slicer/SliceNullnessAnalyzer.java b/java/java-impl/src/com/intellij/slicer/SliceNullnessAnalyzer.java index 37780b1fc2c8..4216d0070bd9 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceNullnessAnalyzer.java +++ b/java/java-impl/src/com/intellij/slicer/SliceNullnessAnalyzer.java @@ -18,6 +18,7 @@ package com.intellij.slicer; import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInsight.PsiEquivalenceUtil; import com.intellij.codeInspection.dataFlow.DfaUtil; +import com.intellij.codeInspection.dataFlow.Nullness; import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.ide.util.treeView.AbstractTreeStructure; import com.intellij.openapi.application.ApplicationManager; @@ -174,16 +175,16 @@ public class SliceNullnessAnalyzer { else { SliceUsage sliceUsage = element.getValue(); final PsiElement value = sliceUsage.getElement(); - DfaUtil.Nullness nullness = ApplicationManager.getApplication().runReadAction(new Computable() { + Nullness nullness = ApplicationManager.getApplication().runReadAction(new Computable() { @Override - public DfaUtil.Nullness compute() { + public Nullness compute() { return checkNullness(value); } }); - if (nullness == DfaUtil.Nullness.NULL) { + if (nullness == Nullness.NULLABLE) { group(element, map, NullAnalysisResult.NULLS).add(value); } - else if (nullness == DfaUtil.Nullness.NOT_NULL) { + else if (nullness == Nullness.NOT_NULL) { group(element, map, NullAnalysisResult.NOT_NULLS).add(value); } else { @@ -210,26 +211,26 @@ public class SliceNullnessAnalyzer { } @NotNull - private static DfaUtil.Nullness checkNullness(final PsiElement element) { + private static Nullness checkNullness(final PsiElement element) { // null PsiElement value = element; if (value instanceof PsiExpression) { value = PsiUtil.deparenthesizeExpression((PsiExpression)value); } if (value instanceof PsiLiteralExpression) { - return ((PsiLiteralExpression)value).getValue() == null ? DfaUtil.Nullness.NULL : DfaUtil.Nullness.NOT_NULL; + return ((PsiLiteralExpression)value).getValue() == null ? Nullness.NULLABLE : Nullness.NOT_NULL; } // not null - if (value instanceof PsiNewExpression) return DfaUtil.Nullness.NOT_NULL; - if (value instanceof PsiThisExpression) return DfaUtil.Nullness.NOT_NULL; + if (value instanceof PsiNewExpression) return Nullness.NOT_NULL; + if (value instanceof PsiThisExpression) return Nullness.NOT_NULL; if (value instanceof PsiMethodCallExpression) { PsiMethod method = ((PsiMethodCallExpression)value).resolveMethod(); - if (method != null && NullableNotNullManager.isNotNull(method)) return DfaUtil.Nullness.NOT_NULL; - if (method != null && NullableNotNullManager.isNullable(method)) return DfaUtil.Nullness.NULL; + if (method != null && NullableNotNullManager.isNotNull(method)) return Nullness.NOT_NULL; + if (method != null && NullableNotNullManager.isNullable(method)) return Nullness.NULLABLE; } if (value instanceof PsiPolyadicExpression && ((PsiPolyadicExpression)value).getOperationTokenType() == JavaTokenType.PLUS) { - return DfaUtil.Nullness.NOT_NULL; // "xxx" + var + return Nullness.NOT_NULL; // "xxx" + var } // unfortunately have to resolve here, since there can be no subnodes @@ -243,23 +244,23 @@ public class SliceNullnessAnalyzer { } if (value instanceof PsiParameter && ((PsiParameter)value).getDeclarationScope() instanceof PsiCatchSection) { // exception thrown is always not null - return DfaUtil.Nullness.NOT_NULL; + return Nullness.NOT_NULL; } if (value instanceof PsiLocalVariable || value instanceof PsiParameter) { - DfaUtil.Nullness result = DfaUtil.checkNullness((PsiVariable)value, context); - if (result != DfaUtil.Nullness.UNKNOWN) { + Nullness result = DfaUtil.checkNullness((PsiVariable)value, context); + if (result != Nullness.UNKNOWN) { return result; } } if (value instanceof PsiModifierListOwner) { - if (NullableNotNullManager.isNotNull((PsiModifierListOwner)value)) return DfaUtil.Nullness.NOT_NULL; - if (NullableNotNullManager.isNullable((PsiModifierListOwner)value)) return DfaUtil.Nullness.NULL; + if (NullableNotNullManager.isNotNull((PsiModifierListOwner)value)) return Nullness.NOT_NULL; + if (NullableNotNullManager.isNullable((PsiModifierListOwner)value)) return Nullness.NULLABLE; } - if (value instanceof PsiEnumConstant) return DfaUtil.Nullness.NOT_NULL; - return DfaUtil.Nullness.UNKNOWN; + if (value instanceof PsiEnumConstant) return Nullness.NOT_NULL; + return Nullness.UNKNOWN; } public static class NullAnalysisResult {