mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-13 21:55:01 +07:00
IDEA-130782 make RemoveUnusedVariableUtil#checkSideEffects Contract(pure) aware
This commit is contained in:
+32
-114
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -16,121 +16,39 @@
|
||||
package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PropertyUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import gnu.trove.THashSet;
|
||||
import com.siyeh.ig.psiutils.SideEffectChecker;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RemoveUnusedVariableUtil {
|
||||
public static final int MAKE_STATEMENT = 1;
|
||||
public static final int DELETE_ALL = 2;
|
||||
public static final int CANCEL = 0;
|
||||
private static final Set<String> ourSideEffectFreeClasses = new THashSet<String>();
|
||||
static {
|
||||
ourSideEffectFreeClasses.add(Object.class.getName());
|
||||
ourSideEffectFreeClasses.add(Short.class.getName());
|
||||
ourSideEffectFreeClasses.add(Character.class.getName());
|
||||
ourSideEffectFreeClasses.add(Byte.class.getName());
|
||||
ourSideEffectFreeClasses.add(Integer.class.getName());
|
||||
ourSideEffectFreeClasses.add(Long.class.getName());
|
||||
ourSideEffectFreeClasses.add(Float.class.getName());
|
||||
ourSideEffectFreeClasses.add(Double.class.getName());
|
||||
ourSideEffectFreeClasses.add(String.class.getName());
|
||||
ourSideEffectFreeClasses.add(StringBuffer.class.getName());
|
||||
ourSideEffectFreeClasses.add(Boolean.class.getName());
|
||||
|
||||
ourSideEffectFreeClasses.add(ArrayList.class.getName());
|
||||
ourSideEffectFreeClasses.add(Date.class.getName());
|
||||
ourSideEffectFreeClasses.add(HashMap.class.getName());
|
||||
ourSideEffectFreeClasses.add(HashSet.class.getName());
|
||||
ourSideEffectFreeClasses.add(Hashtable.class.getName());
|
||||
ourSideEffectFreeClasses.add(LinkedHashMap.class.getName());
|
||||
ourSideEffectFreeClasses.add(LinkedHashSet.class.getName());
|
||||
ourSideEffectFreeClasses.add(LinkedList.class.getName());
|
||||
ourSideEffectFreeClasses.add(Stack.class.getName());
|
||||
ourSideEffectFreeClasses.add(TreeMap.class.getName());
|
||||
ourSideEffectFreeClasses.add(TreeSet.class.getName());
|
||||
ourSideEffectFreeClasses.add(Vector.class.getName());
|
||||
ourSideEffectFreeClasses.add(WeakHashMap.class.getName());
|
||||
public enum RemoveMode {
|
||||
MAKE_STATEMENT,
|
||||
DELETE_ALL,
|
||||
CANCEL
|
||||
}
|
||||
|
||||
static boolean isSideEffectFreeConstructor(PsiNewExpression newExpression) {
|
||||
PsiJavaCodeReferenceElement classReference = newExpression.getClassReference();
|
||||
PsiClass aClass = classReference == null ? null : (PsiClass)classReference.resolve();
|
||||
String qualifiedName = aClass == null ? null : aClass.getQualifiedName();
|
||||
if (qualifiedName == null) return false;
|
||||
if (ourSideEffectFreeClasses.contains(qualifiedName)) return true;
|
||||
|
||||
PsiFile file = aClass.getContainingFile();
|
||||
PsiDirectory directory = file.getContainingDirectory();
|
||||
PsiPackage classPackage = JavaDirectoryService.getInstance().getPackage(directory);
|
||||
String packageName = classPackage == null ? null : classPackage.getQualifiedName();
|
||||
|
||||
// all Throwable descendants from java.lang are side effects free
|
||||
if ("java.lang".equals(packageName) || "java.io".equals(packageName)) {
|
||||
PsiClass throwableClass = JavaPsiFacade.getInstance(aClass.getProject()).findClass("java.lang.Throwable", aClass.getResolveScope());
|
||||
if (throwableClass != null && InheritanceUtil.isInheritorOrSelf(aClass, throwableClass, true)) {
|
||||
return true;
|
||||
public static boolean checkSideEffects(PsiExpression element, PsiVariable variableToIgnore, List<PsiElement> sideEffects) {
|
||||
if (sideEffects == null || element == null) return false;
|
||||
List<PsiElement> writes = new ArrayList<PsiElement>();
|
||||
SideEffectChecker.checkSideEffects(element, writes);
|
||||
if (variableToIgnore != null) {
|
||||
for (int i = writes.size() - 1; i >= 0; i--) {
|
||||
PsiElement write = writes.get(i);
|
||||
if (!(write instanceof PsiAssignmentExpression)) continue;
|
||||
PsiExpression lExpression = ((PsiAssignmentExpression)write).getLExpression();
|
||||
if (lExpression instanceof PsiReference && ((PsiReference)lExpression).resolve() == variableToIgnore) {
|
||||
writes.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
sideEffects.addAll(writes);
|
||||
return !writes.isEmpty();
|
||||
}
|
||||
|
||||
public static boolean checkSideEffects(PsiElement element, PsiVariable variable, List<PsiElement> sideEffects) {
|
||||
if (sideEffects == null || element == null) return false;
|
||||
if (element instanceof PsiMethodCallExpression) {
|
||||
final PsiMethod psiMethod = ((PsiMethodCallExpression)element).resolveMethod();
|
||||
if (psiMethod == null || !PropertyUtil.isSimpleGetter(psiMethod) && !PropertyUtil.isSimpleSetter(psiMethod)) {
|
||||
sideEffects.add(element);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (element instanceof PsiNewExpression) {
|
||||
PsiNewExpression newExpression = (PsiNewExpression)element;
|
||||
if (newExpression.getArrayDimensions().length == 0
|
||||
&& newExpression.getArrayInitializer() == null
|
||||
&& !isSideEffectFreeConstructor(newExpression)) {
|
||||
sideEffects.add(element);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (element instanceof PsiAssignmentExpression
|
||||
&& !(((PsiAssignmentExpression)element).getLExpression() instanceof PsiReferenceExpression
|
||||
&& ((PsiReferenceExpression)((PsiAssignmentExpression)element).getLExpression()).resolve() == variable)) {
|
||||
sideEffects.add(element);
|
||||
return true;
|
||||
}
|
||||
|
||||
IElementType tokenType = null;
|
||||
PsiExpression operand = null;
|
||||
if (element instanceof PsiPrefixExpression) {
|
||||
operand = ((PsiPrefixExpression)element).getOperand();
|
||||
tokenType = ((PsiPrefixExpression)element).getOperationTokenType();
|
||||
} else if (element instanceof PsiPostfixExpression) {
|
||||
operand = ((PsiPostfixExpression)element).getOperand();
|
||||
tokenType = ((PsiPostfixExpression)element).getOperationTokenType();
|
||||
}
|
||||
|
||||
if (JavaTokenType.MINUSMINUS.equals(tokenType) || JavaTokenType.PLUSPLUS.equals(tokenType)) {
|
||||
operand = PsiUtil.deparenthesizeExpression(operand);
|
||||
if (!(operand instanceof PsiReferenceExpression && ((PsiReferenceExpression)operand).resolve() == variable)) {
|
||||
sideEffects.add(element);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
PsiElement[] children = element.getChildren();
|
||||
|
||||
for (PsiElement child : children) {
|
||||
checkSideEffects(child, variable, sideEffects);
|
||||
}
|
||||
return !sideEffects.isEmpty();
|
||||
}
|
||||
|
||||
static PsiElement replaceElementWithExpression(PsiExpression expression,
|
||||
PsiElementFactory factory,
|
||||
PsiElement element) throws IncorrectOperationException {
|
||||
@@ -177,7 +95,7 @@ public class RemoveUnusedVariableUtil {
|
||||
}
|
||||
}
|
||||
|
||||
static void deleteReferences(PsiVariable variable, List<PsiElement> references, int mode) throws IncorrectOperationException {
|
||||
static void deleteReferences(PsiVariable variable, List<PsiElement> references, @NotNull RemoveMode mode) throws IncorrectOperationException {
|
||||
for (PsiElement expression : references) {
|
||||
processUsage(expression, variable, null, mode);
|
||||
}
|
||||
@@ -198,7 +116,7 @@ public class RemoveUnusedVariableUtil {
|
||||
* null if read usage found (may happen if interval between fix creation in invoke() call was long enough)
|
||||
* @throws com.intellij.util.IncorrectOperationException
|
||||
*/
|
||||
static Boolean processUsage(PsiElement element, PsiVariable variable, List<PsiElement> sideEffects, int deleteMode)
|
||||
static Boolean processUsage(PsiElement element, PsiVariable variable, List<PsiElement> sideEffects, @NotNull RemoveMode deleteMode)
|
||||
throws IncorrectOperationException {
|
||||
if (!element.isValid()) return null;
|
||||
PsiElementFactory factory = JavaPsiFacade.getInstance(variable.getProject()).getElementFactory();
|
||||
@@ -216,8 +134,8 @@ public class RemoveUnusedVariableUtil {
|
||||
// replace assignment with expression and resimplify
|
||||
boolean sideEffectFound = checkSideEffects(rExpression, variable, sideEffects);
|
||||
if (!(element.getParent() instanceof PsiExpressionStatement) || PsiUtil.isStatement(rExpression)) {
|
||||
if (deleteMode == MAKE_STATEMENT ||
|
||||
deleteMode == DELETE_ALL && !(element.getParent() instanceof PsiExpressionStatement)) {
|
||||
if (deleteMode == RemoveMode.MAKE_STATEMENT ||
|
||||
deleteMode == RemoveMode.DELETE_ALL && !(element.getParent() instanceof PsiExpressionStatement)) {
|
||||
element = replaceElementWithExpression(rExpression, factory, element);
|
||||
while (element.getParent() instanceof PsiParenthesizedExpression) {
|
||||
element = element.getParent().replace(element);
|
||||
@@ -226,19 +144,19 @@ public class RemoveUnusedVariableUtil {
|
||||
collectReferences(element, variable, references);
|
||||
deleteReferences(variable, references, deleteMode);
|
||||
}
|
||||
else if (deleteMode == DELETE_ALL) {
|
||||
else if (deleteMode == RemoveMode.DELETE_ALL) {
|
||||
deleteWholeStatement(element, factory);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (deleteMode != CANCEL) {
|
||||
if (deleteMode != RemoveMode.CANCEL) {
|
||||
deleteWholeStatement(element, factory);
|
||||
}
|
||||
return !sideEffectFound;
|
||||
}
|
||||
}
|
||||
else if (element instanceof PsiExpressionStatement && deleteMode != CANCEL) {
|
||||
else if (element instanceof PsiExpressionStatement && deleteMode != RemoveMode.CANCEL) {
|
||||
final PsiElement parent = element.getParent();
|
||||
if (parent instanceof PsiIfStatement || parent instanceof PsiLoopStatement && ((PsiLoopStatement)parent).getBody() == element) {
|
||||
element.replace(JavaPsiFacade.getElementFactory(element.getProject()).createStatementFromText(";", element));
|
||||
@@ -257,19 +175,19 @@ public class RemoveUnusedVariableUtil {
|
||||
&&
|
||||
!(variable.getParent() instanceof PsiDeclarationStatement &&
|
||||
((PsiDeclarationStatement)variable.getParent()).getDeclaredElements().length > 1)) {
|
||||
if (deleteMode == MAKE_STATEMENT) {
|
||||
if (deleteMode == RemoveMode.MAKE_STATEMENT) {
|
||||
element = element.replace(createStatementIfNeeded(expression, factory, element));
|
||||
List<PsiElement> references = new ArrayList<PsiElement>();
|
||||
collectReferences(element, variable, references);
|
||||
deleteReferences(variable, references, deleteMode);
|
||||
}
|
||||
else if (deleteMode == DELETE_ALL) {
|
||||
else if (deleteMode == RemoveMode.DELETE_ALL) {
|
||||
element.delete();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (deleteMode != CANCEL) {
|
||||
if (deleteMode != RemoveMode.CANCEL) {
|
||||
if (element instanceof PsiField) {
|
||||
((PsiField)element).normalizeDeclaration();
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -163,9 +163,9 @@ public class AccessStaticViaInstanceFix extends LocalQuickFixAndIntentionActionO
|
||||
};
|
||||
dialog.show();
|
||||
int res = dialog.getExitCode();
|
||||
if (res == RemoveUnusedVariableUtil.CANCEL) return false;
|
||||
if (res == RemoveUnusedVariableUtil.RemoveMode.CANCEL.ordinal()) return false;
|
||||
try {
|
||||
if (res == RemoveUnusedVariableUtil.MAKE_STATEMENT) {
|
||||
if (res == RemoveUnusedVariableUtil.RemoveMode.MAKE_STATEMENT.ordinal()) {
|
||||
final PsiStatement statementFromText = factory.createStatementFromText(qualifierExpression.getText() + ";", null);
|
||||
final PsiStatement statement = PsiTreeUtil.getParentOfType(myExpression, PsiStatement.class);
|
||||
statement.getParent().addBefore(statementFromText, statement);
|
||||
|
||||
+16
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -27,12 +27,15 @@ import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.psi.util.PsiExpressionTrimRenderer;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RemoveUnusedVariableFix implements IntentionAction {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.RemoveUnusedVariableFix");
|
||||
@@ -83,7 +86,7 @@ public class RemoveUnusedVariableFix implements IntentionAction {
|
||||
references.add(myVariable);
|
||||
// check for side effects
|
||||
for (PsiElement element : references) {
|
||||
Boolean result = RemoveUnusedVariableUtil.processUsage(element, myVariable, sideEffects, RemoveUnusedVariableUtil.CANCEL);
|
||||
Boolean result = RemoveUnusedVariableUtil.processUsage(element, myVariable, sideEffects, RemoveUnusedVariableUtil.RemoveMode.CANCEL);
|
||||
if (result == null) return;
|
||||
canCopeWithSideEffects[0] &= result;
|
||||
}
|
||||
@@ -92,7 +95,8 @@ public class RemoveUnusedVariableFix implements IntentionAction {
|
||||
LOG.error(e);
|
||||
}
|
||||
|
||||
final int deleteMode = showSideEffectsWarning(sideEffects, myVariable, editor, canCopeWithSideEffects[0]);
|
||||
final RemoveUnusedVariableUtil.RemoveMode
|
||||
deleteMode = showSideEffectsWarning(sideEffects, myVariable, editor, canCopeWithSideEffects[0]);
|
||||
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
@@ -107,17 +111,17 @@ public class RemoveUnusedVariableFix implements IntentionAction {
|
||||
});
|
||||
}
|
||||
|
||||
public static int showSideEffectsWarning(List<PsiElement> sideEffects,
|
||||
public static RemoveUnusedVariableUtil.RemoveMode showSideEffectsWarning(List<PsiElement> sideEffects,
|
||||
PsiVariable variable,
|
||||
Editor editor,
|
||||
boolean canCopeWithSideEffects,
|
||||
@NonNls String beforeText,
|
||||
@NonNls String afterText) {
|
||||
if (sideEffects.isEmpty()) return RemoveUnusedVariableUtil.DELETE_ALL;
|
||||
if (sideEffects.isEmpty()) return RemoveUnusedVariableUtil.RemoveMode.DELETE_ALL;
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
return canCopeWithSideEffects
|
||||
? RemoveUnusedVariableUtil.MAKE_STATEMENT
|
||||
: RemoveUnusedVariableUtil.DELETE_ALL;
|
||||
? RemoveUnusedVariableUtil.RemoveMode.MAKE_STATEMENT
|
||||
: RemoveUnusedVariableUtil.RemoveMode.DELETE_ALL;
|
||||
}
|
||||
Project project = editor.getProject();
|
||||
HighlightManager highlightManager = HighlightManager.getInstance(project);
|
||||
@@ -128,10 +132,11 @@ public class RemoveUnusedVariableFix implements IntentionAction {
|
||||
|
||||
SideEffectWarningDialog dialog = new SideEffectWarningDialog(project, false, variable, beforeText, afterText, canCopeWithSideEffects);
|
||||
dialog.show();
|
||||
return dialog.getExitCode();
|
||||
int code = dialog.getExitCode();
|
||||
return RemoveUnusedVariableUtil.RemoveMode.values()[code];
|
||||
}
|
||||
|
||||
private static int showSideEffectsWarning(List<PsiElement> sideEffects,
|
||||
private static RemoveUnusedVariableUtil.RemoveMode showSideEffectsWarning(List<PsiElement> sideEffects,
|
||||
PsiVariable variable,
|
||||
Editor editor,
|
||||
boolean canCopeWithSideEffects) {
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -63,7 +63,7 @@ public class SideEffectWarningDialog extends DialogWrapper {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
close(RemoveUnusedVariableUtil.DELETE_ALL);
|
||||
close(RemoveUnusedVariableUtil.RemoveMode.DELETE_ALL.ordinal());
|
||||
}
|
||||
|
||||
};
|
||||
@@ -76,7 +76,7 @@ public class SideEffectWarningDialog extends DialogWrapper {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
close(RemoveUnusedVariableUtil.MAKE_STATEMENT);
|
||||
close(RemoveUnusedVariableUtil.RemoveMode.MAKE_STATEMENT.ordinal());
|
||||
}
|
||||
};
|
||||
actions.add(makeStmtAction);
|
||||
@@ -110,7 +110,7 @@ public class SideEffectWarningDialog extends DialogWrapper {
|
||||
|
||||
@Override
|
||||
public void doCancelAction() {
|
||||
close(RemoveUnusedVariableUtil.CANCEL);
|
||||
close(RemoveUnusedVariableUtil.RemoveMode.CANCEL.ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -46,16 +46,19 @@ public class RemoveInitializerFix implements LocalQuickFix {
|
||||
if (!(psiInitializer.getParent() instanceof PsiVariable)) return;
|
||||
|
||||
final PsiVariable variable = (PsiVariable)psiInitializer.getParent();
|
||||
sideEffectAwareRemove(project, psiInitializer, psiInitializer, variable);
|
||||
sideEffectAwareRemove(project, (PsiExpression)psiInitializer, psiInitializer, variable);
|
||||
}
|
||||
|
||||
protected void sideEffectAwareRemove(Project project, PsiElement psiInitializer, PsiElement elementToDelete, PsiVariable variable) {
|
||||
protected static void sideEffectAwareRemove(Project project,
|
||||
PsiExpression psiInitializer,
|
||||
PsiElement elementToDelete,
|
||||
PsiVariable variable) {
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(elementToDelete.getContainingFile())) return;
|
||||
|
||||
final PsiElement declaration = variable.getParent();
|
||||
final List<PsiElement> sideEffects = new ArrayList<PsiElement>();
|
||||
boolean hasSideEffects = RemoveUnusedVariableUtil.checkSideEffects(psiInitializer, variable, sideEffects);
|
||||
int res = RemoveUnusedVariableUtil.DELETE_ALL;
|
||||
RemoveUnusedVariableUtil.RemoveMode res = RemoveUnusedVariableUtil.RemoveMode.DELETE_ALL;
|
||||
if (hasSideEffects) {
|
||||
hasSideEffects = PsiUtil.isStatement(psiInitializer);
|
||||
res = RemoveUnusedVariableFix.showSideEffectsWarning(sideEffects, variable,
|
||||
@@ -65,15 +68,14 @@ public class RemoveInitializerFix implements LocalQuickFix {
|
||||
" " +
|
||||
variable.getName() +
|
||||
";<br>" +
|
||||
PsiExpressionTrimRenderer
|
||||
.render((PsiExpression)psiInitializer)
|
||||
PsiExpressionTrimRenderer.render(psiInitializer)
|
||||
);
|
||||
}
|
||||
try {
|
||||
if (res == RemoveUnusedVariableUtil.DELETE_ALL) {
|
||||
if (res == RemoveUnusedVariableUtil.RemoveMode.DELETE_ALL) {
|
||||
elementToDelete.delete();
|
||||
}
|
||||
else if (res == RemoveUnusedVariableUtil.MAKE_STATEMENT) {
|
||||
else if (res == RemoveUnusedVariableUtil.RemoveMode.MAKE_STATEMENT) {
|
||||
final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
|
||||
final PsiStatement statementFromText = factory.createStatementFromText(psiInitializer.getText() + ";", null);
|
||||
final PsiElement parent = elementToDelete.getParent();
|
||||
|
||||
+2
-1
@@ -4,7 +4,8 @@ import java.io.*;
|
||||
class a {
|
||||
int k;
|
||||
private void run() {
|
||||
<caret>while (1 > 0) ;
|
||||
k = 9;
|
||||
while (1 > 0) ;
|
||||
for (;; ) ;
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Remove variable 'i'" "true"
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
class a {
|
||||
private void run() {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private Object dodo() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Remove variable 'i'" "true"
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
class a {
|
||||
private void run() {
|
||||
Object <caret>i = dodo();
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private Object dodo() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+74
-15
@@ -19,12 +19,40 @@ import com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PropertyUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SideEffectChecker {
|
||||
private static final Set<String> ourSideEffectFreeClasses = new THashSet<String>(Arrays.asList(
|
||||
Object.class.getName(),
|
||||
Short.class.getName(),
|
||||
Character.class.getName(),
|
||||
Byte.class.getName(),
|
||||
Integer.class.getName(),
|
||||
Long.class.getName(),
|
||||
Float.class.getName(),
|
||||
Double.class.getName(),
|
||||
String.class.getName(),
|
||||
StringBuffer.class.getName(),
|
||||
Boolean.class.getName(),
|
||||
|
||||
ArrayList.class.getName(),
|
||||
Date.class.getName(),
|
||||
HashMap.class.getName(),
|
||||
HashSet.class.getName(),
|
||||
Hashtable.class.getName(),
|
||||
LinkedHashMap.class.getName(),
|
||||
LinkedHashSet.class.getName(),
|
||||
LinkedList.class.getName(),
|
||||
Stack.class.getName(),
|
||||
TreeMap.class.getName(),
|
||||
TreeSet.class.getName(),
|
||||
Vector.class.getName(),
|
||||
WeakHashMap.class.getName()));
|
||||
|
||||
private SideEffectChecker() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static boolean mayHaveSideEffects(@NotNull PsiExpression exp) {
|
||||
@@ -33,13 +61,22 @@ public class SideEffectChecker {
|
||||
return visitor.mayHaveSideEffects();
|
||||
}
|
||||
|
||||
private static class SideEffectsVisitor extends JavaRecursiveElementWalkingVisitor {
|
||||
public static boolean checkSideEffects(@NotNull PsiExpression element, @NotNull List<PsiElement> sideEffects) {
|
||||
final SideEffectsVisitor visitor = new SideEffectsVisitor();
|
||||
element.accept(visitor);
|
||||
if (visitor.sideEffect != null) {
|
||||
sideEffects.add(visitor.sideEffect);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean mayHaveSideEffects = false;
|
||||
private static class SideEffectsVisitor extends JavaRecursiveElementWalkingVisitor {
|
||||
PsiElement sideEffect;
|
||||
|
||||
@Override
|
||||
public void visitElement(@NotNull PsiElement element) {
|
||||
if (!mayHaveSideEffects) {
|
||||
if (sideEffect == null) {
|
||||
super.visitElement(element);
|
||||
}
|
||||
}
|
||||
@@ -47,17 +84,17 @@ public class SideEffectChecker {
|
||||
@Override
|
||||
public void visitAssignmentExpression(
|
||||
@NotNull PsiAssignmentExpression expression) {
|
||||
if (mayHaveSideEffects) {
|
||||
if (sideEffect != null) {
|
||||
return;
|
||||
}
|
||||
super.visitAssignmentExpression(expression);
|
||||
mayHaveSideEffects = true;
|
||||
sideEffect = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(
|
||||
@NotNull PsiMethodCallExpression expression) {
|
||||
if (mayHaveSideEffects) {
|
||||
if (sideEffect != null) {
|
||||
return;
|
||||
}
|
||||
super.visitMethodCallExpression(expression);
|
||||
@@ -66,48 +103,70 @@ public class SideEffectChecker {
|
||||
return;
|
||||
}
|
||||
|
||||
mayHaveSideEffects = true;
|
||||
sideEffect = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNewExpression(@NotNull PsiNewExpression expression) {
|
||||
if (mayHaveSideEffects) {
|
||||
if (sideEffect != null) {
|
||||
return;
|
||||
}
|
||||
super.visitNewExpression(expression);
|
||||
mayHaveSideEffects = true;
|
||||
sideEffect = isSideEffectFreeConstructor(expression) ? null : expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPostfixExpression(
|
||||
@NotNull PsiPostfixExpression expression) {
|
||||
if (mayHaveSideEffects) {
|
||||
if (sideEffect != null) {
|
||||
return;
|
||||
}
|
||||
super.visitPostfixExpression(expression);
|
||||
final IElementType tokenType = expression.getOperationTokenType();
|
||||
if (tokenType.equals(JavaTokenType.PLUSPLUS) ||
|
||||
tokenType.equals(JavaTokenType.MINUSMINUS)) {
|
||||
mayHaveSideEffects = true;
|
||||
sideEffect = expression;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPrefixExpression(
|
||||
@NotNull PsiPrefixExpression expression) {
|
||||
if (mayHaveSideEffects) {
|
||||
if (sideEffect != null) {
|
||||
return;
|
||||
}
|
||||
super.visitPrefixExpression(expression);
|
||||
final IElementType tokenType = expression.getOperationTokenType();
|
||||
if (tokenType.equals(JavaTokenType.PLUSPLUS) ||
|
||||
tokenType.equals(JavaTokenType.MINUSMINUS)) {
|
||||
mayHaveSideEffects = true;
|
||||
sideEffect = expression;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean mayHaveSideEffects() {
|
||||
return mayHaveSideEffects;
|
||||
return sideEffect != null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isSideEffectFreeConstructor(@NotNull PsiNewExpression newExpression) {
|
||||
PsiJavaCodeReferenceElement classReference = newExpression.getClassReference();
|
||||
PsiClass aClass = classReference == null ? null : (PsiClass)classReference.resolve();
|
||||
String qualifiedName = aClass == null ? null : aClass.getQualifiedName();
|
||||
if (qualifiedName == null) return false;
|
||||
if (ourSideEffectFreeClasses.contains(qualifiedName)) return true;
|
||||
|
||||
PsiFile file = aClass.getContainingFile();
|
||||
PsiDirectory directory = file.getContainingDirectory();
|
||||
PsiPackage classPackage = JavaDirectoryService.getInstance().getPackage(directory);
|
||||
String packageName = classPackage == null ? null : classPackage.getQualifiedName();
|
||||
|
||||
// all Throwable descendants from java.lang are side effects free
|
||||
if ("java.lang".equals(packageName) || "java.io".equals(packageName)) {
|
||||
PsiClass throwableClass = JavaPsiFacade.getInstance(aClass.getProject()).findClass("java.lang.Throwable", aClass.getResolveScope());
|
||||
if (throwableClass != null && com.intellij.psi.util.InheritanceUtil.isInheritorOrSelf(aClass, throwableClass, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user