single dfa variable nullability

This commit is contained in:
peter
2013-02-03 22:42:56 +01:00
parent 83231fb653
commit 4587183209
8 changed files with 101 additions and 78 deletions
@@ -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);
}
@@ -86,17 +86,17 @@ public class DfaUtil {
return expressions == null ? Collections.<PsiExpression>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;
}
@@ -40,27 +40,23 @@ import java.util.Set;
public class DfaVariableState implements Cloneable {
private final Set<DfaTypeValue> myInstanceofValues;
private final Set<DfaTypeValue> myNotInstanceofValues;
private boolean myNullable = false;
private final boolean myVariableIsDeclaredNotNull;
private Nullness myNullability;
public DfaVariableState(@NotNull DfaVariableValue dfaVar) {
myInstanceofValues = new HashSet<DfaTypeValue>();
myNotInstanceofValues = new HashSet<DfaTypeValue>();
Boolean nullability = dfaVar.getInherentNullability();
myNullable = nullability == Boolean.TRUE;
myVariableIsDeclaredNotNull = nullability == Boolean.FALSE;
myNullability = dfaVar.getInherentNullability();
}
protected DfaVariableState(final DfaVariableState toClone) {
myInstanceofValues = new THashSet<DfaTypeValue>(toClone.myInstanceofValues);
myNotInstanceofValues = new THashSet<DfaTypeValue>(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) {
@@ -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
}
@@ -40,26 +40,26 @@ public class StandardInstructionVisitor extends InstructionVisitor {
private final Set<BinopInstruction> myCanBeNullInInstanceof = new THashSet<BinopInstruction>();
private final Set<PsiElement> myNotToReportReachability = new THashSet<PsiElement>();
private final Set<InstanceofInstruction> myUsefulInstanceofs = new THashSet<InstanceofInstruction>();
private final FactoryMap<MethodCallInstruction, Map<PsiExpression, Boolean>> myParametersNullability = new FactoryMap<MethodCallInstruction, Map<PsiExpression, Boolean>>() {
private final FactoryMap<MethodCallInstruction, Map<PsiExpression, Nullness>> myParametersNullability = new FactoryMap<MethodCallInstruction, Map<PsiExpression, Nullness>>() {
@Nullable
@Override
protected Map<PsiExpression, Boolean> create(MethodCallInstruction key) {
protected Map<PsiExpression, Nullness> create(MethodCallInstruction key) {
return calcParameterNullability(key.getCallExpression());
}
};
private final FactoryMap<MethodCallInstruction, Boolean> myReturnTypeNullability = new FactoryMap<MethodCallInstruction, Boolean>() {
private final FactoryMap<MethodCallInstruction, Nullness> myReturnTypeNullability = new FactoryMap<MethodCallInstruction, Nullness>() {
@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<PsiExpression, Boolean> calcParameterNullability(@Nullable PsiCallExpression callExpression) {
private static Map<PsiExpression, Nullness> 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<PsiExpression, Boolean> map = ContainerUtil.newHashMap();
Map<PsiExpression, Nullness> 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<PsiExpression, Boolean> map = myParametersNullability.get(instruction);
Map<PsiExpression, Nullness> 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) {
@@ -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() {
@@ -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<Boolean> 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;
}
@@ -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<DfaUtil.Nullness>() {
Nullness nullness = ApplicationManager.getApplication().runReadAction(new Computable<Nullness>() {
@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 {