don't flush fields for pure method and getter calls (IDEA-117449)

This commit is contained in:
peter
2014-03-05 18:40:52 +01:00
parent 80049dfc29
commit 6166a0e4fb
6 changed files with 59 additions and 12 deletions

View File

@@ -1602,6 +1602,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
return Collections.emptyList();
}
@Nullable
public static PsiAnnotation findContractAnnotation(PsiMethod method) {
return AnnotationUtil.findAnnotation(method, ORG_JETBRAINS_ANNOTATIONS_CONTRACT);
}
@@ -1741,6 +1742,9 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
if (psiVariable != null) {
DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(psiVariable, false);
addInstruction(new FlushVariableInstruction(dfaVariable));
if (psiVariable instanceof PsiField) {
addInstruction(new FlushVariableInstruction(null));
}
}
}
@@ -1774,6 +1778,9 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
if (psiVariable != null) {
DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(psiVariable, false);
addInstruction(new FlushVariableInstruction(dfaVariable));
if (psiVariable instanceof PsiField) {
addInstruction(new FlushVariableInstruction(null));
}
}
}
}

View File

@@ -24,12 +24,11 @@
*/
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.codeInsight.AnnotationUtil;
import com.intellij.codeInspection.dataFlow.*;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -56,17 +55,26 @@ public class MethodCallInstruction extends Instruction {
myPrecalculatedReturnValue = null;
}
public MethodCallInstruction(@NotNull PsiCallExpression context, @Nullable DfaValue precalculatedReturnValue) {
myContext = context;
public MethodCallInstruction(@NotNull PsiCallExpression call, @Nullable DfaValue precalculatedReturnValue) {
myContext = call;
myMethodType = MethodType.REGULAR_METHOD_CALL;
myCall = context;
final PsiExpressionList argList = context.getArgumentList();
myCall = call;
final PsiExpressionList argList = call.getArgumentList();
myArgs = argList != null ? argList.getExpressions() : PsiExpression.EMPTY_ARRAY;
myType = myCall.getType();
myShouldFlushFields = !(myCall instanceof PsiNewExpression && myType != null && myType.getArrayDimensions() > 0);
myShouldFlushFields = !(myCall instanceof PsiNewExpression && myType != null && myType.getArrayDimensions() > 0) && !isPureCall(call);
myPrecalculatedReturnValue = precalculatedReturnValue;
}
private static boolean isPureCall(PsiCallExpression call) {
PsiMethod method = call.resolveMethod();
if (method == null) return false;
PsiAnnotation anno = ControlFlowAnalyzer.findContractAnnotation(method);
if (anno != null && Boolean.TRUE.equals(AnnotationUtil.getBooleanAttributeValue(anno, "pure"))) return true;
return PropertyUtil.isSimplePropertyGetter(method);
}
@Nullable
public PsiType getResultType() {
return myType;

View File

@@ -436,13 +436,13 @@ public class AnnotationUtil {
}
@Nullable
public static String getStringAttributeValue(PsiAnnotation anno, @Nullable final String attributeName) {
public static String getStringAttributeValue(@NotNull PsiAnnotation anno, @Nullable final String attributeName) {
PsiAnnotationMemberValue attrValue = anno.findAttributeValue(attributeName);
Object constValue = JavaPsiFacade.getInstance(anno.getProject()).getConstantEvaluationHelper().computeConstantExpression(attrValue);
return constValue instanceof String ? (String)constValue : null;
}
@Nullable
public static Boolean getBooleanAttributeValue(PsiAnnotation anno, @Nullable final String attributeName) {
public static Boolean getBooleanAttributeValue(@NotNull PsiAnnotation anno, @Nullable final String attributeName) {
PsiAnnotationMemberValue attrValue = anno.findAttributeValue(attributeName);
Object constValue = JavaPsiFacade.getInstance(anno.getProject()).getConstantEvaluationHelper().computeConstantExpression(attrValue);
return constValue instanceof Boolean ? (Boolean)constValue : null;

View File

@@ -44,7 +44,7 @@ public class PropertyUtil {
private PropertyUtil() {
}
public static boolean isSimplePropertyGetter(PsiMethod method) {
public static boolean isSimplePropertyGetter(@NotNull PsiMethod method) {
return hasGetterName(method) && method.getParameterList().getParametersCount() == 0;
}

View File

@@ -0,0 +1,31 @@
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
class Doo {
@Nullable
Object getMethod() {return null;}
boolean isSomething() { return false;}
@Contract(pure=true)
boolean pureSomething() { return false;}
public void main2() {
if (getMethod() == null && !isSomething()) {
return;
} else {
System.out.println(<warning descr="Method invocation 'getMethod().hashCode()' may produce 'java.lang.NullPointerException'">getMethod().hashCode()</warning>);
}
}
public void main3() {
if (getMethod() == null && !pureSomething()) {
return;
} else {
System.out.println(<warning descr="Method invocation 'getMethod().hashCode()' may produce 'java.lang.NullPointerException'">getMethod().hashCode()</warning>);
}
}
}

View File

@@ -306,6 +306,7 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase {
public void testAndEquals() { doTest(); }
public void testUnusedCallDoesNotMakeUnknown() { doTest(); }
public void testGettersAndPureNoFlushing() { doTest(); }
public void testParametersAreNonnullByDefault() {
myFixture.addClass("package javax.annotation; public @interface ParametersAreNonnullByDefault {}");