IDEA-85993 Nullable analysis works incorrectly with inner classes

This commit is contained in:
peter
2012-10-29 12:57:23 +01:00
parent 03ab1d39cb
commit 733b09331a
6 changed files with 58 additions and 14 deletions
@@ -213,7 +213,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
PsiElement[] elements = statement.getDeclaredElements();
for (PsiElement element : elements) {
if (element instanceof PsiClass) {
element.accept(this);
addInstruction(new EmptyInstruction(element));
}
else if (element instanceof PsiVariable) {
PsiVariable variable = (PsiVariable)element;
@@ -306,7 +306,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
PsiStatement body = ((PsiForeachStatement)continuedStatement).getBody();
offset = myPass1Flow.getEndOffset(body);
}
Instruction instruction = offset == -1 ? new EmptyInstruction() : new GotoInstruction(offset);
Instruction instruction = offset == -1 ? new EmptyInstruction(null) : new GotoInstruction(offset);
addInstruction(instruction);
}
finishElement(statement);
@@ -25,6 +25,7 @@
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInspection.dataFlow.instructions.BranchingInstruction;
import com.intellij.codeInspection.dataFlow.instructions.EmptyInstruction;
import com.intellij.codeInspection.dataFlow.instructions.Instruction;
import com.intellij.codeInspection.dataFlow.instructions.MethodCallInstruction;
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
@@ -35,6 +36,7 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -71,11 +73,11 @@ public class DataFlowRunner {
protected Collection<DfaMemoryState> createInitialStates(@NotNull PsiElement psiBlock, InstructionVisitor visitor) {
if (psiBlock.getParent() instanceof PsiMethod) {
final PsiClass containingClass = ((PsiMethod)psiBlock.getParent()).getContainingClass();
if (containingClass instanceof PsiAnonymousClass) {
final PsiElement newExpression = containingClass.getParent();
final PsiCodeBlock block = DfaUtil.getTopmostBlockInSameClass(newExpression);
if (newExpression instanceof PsiNewExpression && block != null) {
final EnvironmentalInstructionVisitor envVisitor = new EnvironmentalInstructionVisitor(visitor, (PsiNewExpression)newExpression);
if (containingClass != null && PsiUtil.isLocalOrAnonymousClass(containingClass)) {
final PsiElement parent = containingClass.getParent();
final PsiCodeBlock block = DfaUtil.getTopmostBlockInSameClass(parent);
if ((parent instanceof PsiNewExpression || parent instanceof PsiDeclarationStatement) && block != null) {
final EnvironmentalInstructionVisitor envVisitor = new EnvironmentalInstructionVisitor(visitor, parent);
final RunnerResult result = analyzeMethod(block, envVisitor);
if (result == RunnerResult.OK) {
final Collection<DfaMemoryState> closureStates = envVisitor.getClosureStates();
@@ -225,22 +227,32 @@ public class DataFlowRunner {
}
private static class EnvironmentalInstructionVisitor extends DelegatingInstructionVisitor {
private final PsiNewExpression myNewExpression;
private final PsiElement myClassParent;
private final Set<DfaMemoryState> myClosureStates = new THashSet<DfaMemoryState>();
public EnvironmentalInstructionVisitor(@NotNull InstructionVisitor delegate, @NotNull PsiNewExpression newExpression) {
private EnvironmentalInstructionVisitor(InstructionVisitor delegate, PsiElement classParent) {
super(delegate);
myNewExpression = newExpression;
myClassParent = classParent;
}
@Override
public DfaInstructionState[] visitEmptyInstruction(EmptyInstruction instruction, DataFlowRunner runner, DfaMemoryState before) {
checkEnvironment(runner, before, instruction.getAnchor());
return super.visitEmptyInstruction(instruction, runner, before);
}
@Override
public DfaInstructionState[] visitMethodCall(MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
if (myNewExpression == instruction.getCallExpression()) {
checkEnvironment(runner, memState, instruction.getCallExpression());
return super.visitMethodCall(instruction, runner, memState);
}
private void checkEnvironment(DataFlowRunner runner, DfaMemoryState memState, @Nullable PsiElement anchor) {
if (myClassParent == anchor) {
DfaMemoryState copy = memState.createCopy();
copy.flushFields(runner);
myClosureStates.add(copy);
}
return super.visitMethodCall(instruction, runner, memState);
}
@NotNull
@@ -161,4 +161,7 @@ public abstract class InstructionVisitor {
return nextInstruction(instruction, runner, memState);
}
public DfaInstructionState[] visitEmptyInstruction(EmptyInstruction instruction, DataFlowRunner runner, DfaMemoryState before) {
return nextInstruction(instruction, runner, before);
}
}
@@ -25,14 +25,25 @@
package com.intellij.codeInspection.dataFlow.instructions;
import com.intellij.codeInspection.dataFlow.*;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.Nullable;
public class EmptyInstruction extends Instruction {
@Nullable private final PsiElement myAnchor;
public EmptyInstruction(@Nullable PsiElement anchor) {
myAnchor = anchor;
}
@Nullable
public PsiElement getAnchor() {
return myAnchor;
}
@Override
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
Instruction nextInstruction = runner.getInstruction(getIndex() + 1);
return new DfaInstructionState[] {new DfaInstructionState(nextInstruction, stateBefore)};
return visitor.visitEmptyInstruction(this, runner, stateBefore);
}
public String toString() {
@@ -0,0 +1,17 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class Test {
void f(@Nullable final Object x) {
if (x != null) {
class C {
C(@NotNull Object x) {
}
C() {
this(x);
}
}
}
}
}
@@ -47,6 +47,7 @@ public class DataFlowInspectionFixtureTest extends JavaCodeInsightFixtureTestCas
public void testNullableAnonymousParameter() throws Throwable { doTest(); }
public void testNullableAnonymousVolatile() throws Throwable { doTest(); }
public void testNullableAnonymousVolatileNotNull() throws Throwable { doTest(); }
public void testLocalClass() throws Throwable { doTest(); }
public void testFieldInAnonymous() throws Throwable { doTest(); }
public void testNullableField() throws Throwable { doTest(); }