IDEA-130782 make RemoveUnusedVariableUtil#checkSideEffects Contract(pure) aware

This commit is contained in:
Alexey Kudravtsev
2014-10-08 15:01:58 +04:00
parent b9bfd33c9b
commit fc68d2a0d6
9 changed files with 167 additions and 155 deletions
@@ -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();
}
@@ -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);
@@ -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) {
@@ -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();
@@ -4,7 +4,8 @@ import java.io.*;
class a {
int k;
private void run() {
<caret>while (1 > 0) ;
k = 9;
while (1 > 0) ;
for (;; ) ;
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}