purity inference: be more careful with array assignments

This commit is contained in:
peter
2016-05-23 17:39:02 +02:00
parent b8788cfc2f
commit 8692887d89
2 changed files with 39 additions and 8 deletions
@@ -18,10 +18,10 @@ package com.intellij.codeInspection.dataFlow;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.*;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -57,7 +57,7 @@ public class PurityInference {
body.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitAssignmentExpression(PsiAssignmentExpression expression) {
if (!isLocalVarReference(expression.getLExpression())) {
if (!isLocalVarReference(expression.getLExpression(), method)) {
impureFound.set(true);
}
super.visitAssignmentExpression(expression);
@@ -73,7 +73,7 @@ public class PurityInference {
@Override
public void visitPrefixExpression(PsiPrefixExpression expression) {
if (isMutatingOperation(expression.getOperationTokenType()) && !isLocalVarReference(expression.getOperand())) {
if (isMutatingOperation(expression.getOperationTokenType()) && !isLocalVarReference(expression.getOperand(), method)) {
impureFound.set(true);
}
super.visitPrefixExpression(expression);
@@ -85,7 +85,7 @@ public class PurityInference {
@Override
public void visitPostfixExpression(PsiPostfixExpression expression) {
if (isMutatingOperation(expression.getOperationTokenType()) && !isLocalVarReference(expression.getOperand())) {
if (isMutatingOperation(expression.getOperationTokenType()) && !isLocalVarReference(expression.getOperand(), method)) {
impureFound.set(true);
}
super.visitPostfixExpression(expression);
@@ -107,14 +107,33 @@ public class PurityInference {
return called != null && ControlFlowAnalyzer.isPure(called);
}
private static boolean isLocalVarReference(PsiExpression expression) {
private static boolean isLocalVarReference(PsiExpression expression, PsiMethod scope) {
if (expression instanceof PsiReferenceExpression) {
PsiElement target = ((PsiReferenceExpression)expression).resolve();
return target instanceof PsiLocalVariable || target instanceof PsiParameter;
}
if (expression instanceof PsiArrayAccessExpression) {
return isLocalVarReference(((PsiArrayAccessExpression)expression).getArrayExpression());
PsiExpression array = ((PsiArrayAccessExpression)expression).getArrayExpression();
PsiElement target = array instanceof PsiReferenceExpression ? ((PsiReferenceExpression)array).resolve() : null;
return target instanceof PsiLocalVariable && isLocallyCreatedArray(scope, (PsiLocalVariable)target);
}
return false;
}
private static boolean isLocallyCreatedArray(PsiMethod scope, PsiLocalVariable target) {
PsiExpression initializer = target.getInitializer();
if (initializer != null && !(initializer instanceof PsiNewExpression)) {
return false;
}
for (PsiReference ref : ReferencesSearch.search(target, new LocalSearchScope(scope)).findAll()) {
if (ref instanceof PsiReferenceExpression && PsiUtil.isAccessedForWriting((PsiReferenceExpression)ref)) {
PsiAssignmentExpression assign = PsiTreeUtil.getParentOfType((PsiReferenceExpression)ref, PsiAssignmentExpression.class);
if (assign == null || !(assign.getRExpression() instanceof PsiNewExpression)) {
return false;
}
}
}
return true;
}
}
@@ -92,6 +92,18 @@ int random() { return 2; }
"""
}
public void "test field array assignment as local var"() {
assertPure false, """
int[] randomArray() {
int[] local = i;
local[0] = random();
return local;
}
int random() { return 2; }
int[] i = new int[0];
"""
}
public void "test use explicit pure contract"() {
assertPure true, """
int method() {