IDEA-156245 @NotNull doesn't seem to work for enums

This commit is contained in:
peter
2016-05-18 19:44:27 +02:00
parent 6a17887211
commit e1b69b301e
7 changed files with 62 additions and 28 deletions
@@ -1474,7 +1474,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
}
addConditionalRuntimeThrow();
List<MethodContract> contracts = method instanceof PsiMethod ? getMethodCallContracts((PsiMethod)method, expression) : Collections.<MethodContract>emptyList();
List<MethodContract> contracts = method instanceof PsiMethod ? getMethodCallContracts((PsiMethod)method, expression) : Collections.emptyList();
addInstruction(new MethodCallInstruction(expression, myFactory.createValue(expression), contracts));
if (!contracts.isEmpty()) {
// if a contract resulted in 'fail', handle it
@@ -1552,6 +1552,16 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
return anno != null && Boolean.TRUE.equals(AnnotationUtil.getBooleanAttributeValue(anno, "pure"));
}
@Override
public void visitEnumConstant(PsiEnumConstant enumConstant) {
if (enumConstant.getArgumentList() == null) return;
pushUnknown();
pushConstructorArguments(enumConstant);
addInstruction(new MethodCallInstruction(enumConstant, null, Collections.emptyList()));
addInstruction(new PopInstruction());
}
@Override public void visitNewExpression(PsiNewExpression expression) {
startElement(expression);
@@ -1573,28 +1583,16 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
}
}
addConditionalRuntimeThrow();
addInstruction(new MethodCallInstruction(expression, null, Collections.<MethodContract>emptyList()));
addInstruction(new MethodCallInstruction(expression, null, Collections.emptyList()));
}
else {
final PsiExpressionList args = expression.getArgumentList();
PsiMethod ctr = expression.resolveConstructor();
if (args != null) {
PsiExpression[] params = args.getExpressions();
PsiParameter[] parameters = ctr == null ? null : ctr.getParameterList().getParameters();
for (int i = 0; i < params.length; i++) {
PsiExpression param = params[i];
param.accept(this);
if (parameters != null && i < parameters.length) {
generateBoxingUnboxingInstructionFor(param, parameters[i].getType());
}
}
}
PsiMethod constructor = pushConstructorArguments(expression);
addConditionalRuntimeThrow();
addInstruction(new MethodCallInstruction(expression, null, Collections.<MethodContract>emptyList()));
addInstruction(new MethodCallInstruction(expression, null, Collections.emptyList()));
if (!myCatchStack.isEmpty()) {
addMethodThrows(ctr, expression);
addMethodThrows(constructor, expression);
}
}
@@ -1602,6 +1600,23 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
finishElement(expression);
}
private PsiMethod pushConstructorArguments(PsiConstructorCall call) {
PsiExpressionList args = call.getArgumentList();
PsiMethod ctr = call.resolveConstructor();
if (args != null) {
PsiExpression[] params = args.getExpressions();
PsiParameter[] parameters = ctr == null ? null : ctr.getParameterList().getParameters();
for (int i = 0; i < params.length; i++) {
PsiExpression param = params[i];
param.accept(this);
if (parameters != null && i < parameters.length) {
generateBoxingUnboxingInstructionFor(param, parameters[i].getType());
}
}
}
return ctr;
}
@Override public void visitParenthesizedExpression(PsiParenthesizedExpression expression) {
startElement(expression);
PsiExpression inner = expression.getExpression();
@@ -158,7 +158,7 @@ public class DfaPsiUtil {
return false;
}
PsiCallExpression call = ((MethodCallInstruction)instruction).getCallExpression();
PsiCall call = ((MethodCallInstruction)instruction).getCallExpression();
if (call == null) return false;
if (call instanceof PsiMethodCallExpression &&
@@ -25,7 +25,6 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FList;
@@ -113,7 +112,7 @@ public class DfaUtil {
@Nullable
static PsiElement getClosureInside(Instruction instruction) {
if (instruction instanceof MethodCallInstruction) {
PsiCallExpression anchor = ((MethodCallInstruction)instruction).getCallExpression();
PsiCall anchor = ((MethodCallInstruction)instruction).getCallExpression();
if (anchor instanceof PsiNewExpression) {
return ((PsiNewExpression)anchor).getAnonymousClass();
}
@@ -49,7 +49,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
private final FactoryMap<MethodCallInstruction, Nullness> myReturnTypeNullability = new FactoryMap<MethodCallInstruction, Nullness>() {
@Override
protected Nullness create(MethodCallInstruction key) {
final PsiCallExpression callExpression = key.getCallExpression();
final PsiCall callExpression = key.getCallExpression();
if (callExpression instanceof PsiNewExpression) {
return Nullness.NOT_NULL;
}
@@ -237,7 +237,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
@NotNull final DfaValue qualifier = memState.pop();
boolean unboxing = instruction.getMethodType() == MethodCallInstruction.MethodType.UNBOXING;
NullabilityProblem problem = unboxing ? NullabilityProblem.unboxingNullable : NullabilityProblem.callNPE;
PsiExpression anchor = unboxing ? instruction.getContext() : instruction.getCallExpression();
PsiElement anchor = unboxing ? instruction.getContext() : instruction.getCallExpression();
if (!checkNotNullable(memState, qualifier, problem, anchor)) {
forceNotNull(runner, memState, qualifier);
}
@@ -37,11 +37,11 @@ import java.util.Map;
public class MethodCallInstruction extends Instruction {
@Nullable private final PsiCallExpression myCall;
@Nullable private final PsiCall myCall;
@Nullable private final PsiType myType;
@NotNull private final PsiExpression[] myArgs;
private final boolean myShouldFlushFields;
@NotNull private final PsiExpression myContext;
@NotNull private final PsiElement myContext;
@Nullable private final PsiMethod myTargetMethod;
private final List<MethodContract> myContracts;
private final MethodType myMethodType;
@@ -71,14 +71,14 @@ public class MethodCallInstruction extends Instruction {
myArgRequiredNullability = Collections.emptyMap();
}
public MethodCallInstruction(@NotNull PsiCallExpression call, @Nullable DfaValue precalculatedReturnValue, List<MethodContract> contracts) {
public MethodCallInstruction(@NotNull PsiCall call, @Nullable DfaValue precalculatedReturnValue, List<MethodContract> contracts) {
myContext = call;
myContracts = contracts;
myMethodType = MethodType.REGULAR_METHOD_CALL;
myCall = call;
final PsiExpressionList argList = call.getArgumentList();
myArgs = argList != null ? argList.getExpressions() : PsiExpression.EMPTY_ARRAY;
myType = myCall.getType();
myType = myCall instanceof PsiCallExpression ? ((PsiCallExpression)myCall).getType() : null;
JavaResolveResult result = call.resolveMethodGenerics();
myTargetMethod = (PsiMethod)result.getElement();
@@ -175,12 +175,12 @@ public class MethodCallInstruction extends Instruction {
}
@Nullable
public PsiCallExpression getCallExpression() {
public PsiCall getCallExpression() {
return myCall;
}
@NotNull
public PsiExpression getContext() {
public PsiElement getContext() {
return myContext;
}
@@ -0,0 +1,19 @@
import org.jetbrains.annotations.*;
enum NotNullEnum {
ONE("One"),
TWO(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">null</warning>),
THREE(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">null</warning>) {};
private final @NotNull String param;
NotNullEnum(@NotNull String param) {
this.param = param;
}
@NotNull
public String getParam() {
return param;
}
public static void main(String[] args) {
NotNullEnum one = ONE;
}
}
@@ -84,6 +84,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testEqualsEnumConstant() throws Throwable { doTest(); }
public void testSwitchEnumConstant() { doTest(); }
public void testEnumConstantNotNull() throws Throwable { doTest(); }
public void testCheckEnumConstantConstructor() { doTest(); }
public void testCompareToEnumConstant() throws Throwable { doTest(); }
public void testEqualsConstant() throws Throwable { doTest(); }
public void testDontSaveTypeValue() { doTest(); }