Inline inner: show error message as soon as possible (IDEA-39083)

This commit is contained in:
anna
2010-01-29 17:07:23 +03:00
parent de02933e05
commit 55b9c7d053
4 changed files with 62 additions and 79 deletions
@@ -20,6 +20,8 @@ import com.intellij.lang.StdLanguages;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
@@ -38,6 +40,11 @@ import java.util.Collection;
* @author yole
*/
public class InlineToAnonymousClassHandler extends JavaInlineActionHandler {
static final ElementPattern ourCatchClausePattern = PlatformPatterns.psiElement(PsiTypeElement.class).withParent(PlatformPatterns.psiElement(PsiParameter.class).withParent(
PlatformPatterns.psiElement(PsiCatchSection.class)));
static final ElementPattern ourThrowsClausePattern = PlatformPatterns.psiElement().withParent(PlatformPatterns.psiElement(PsiReferenceList.class).withFirstChild(
PlatformPatterns.psiElement().withText(PsiKeyword.THROWS)));
@Override
public boolean isEnabledOnElement(PsiElement element) {
return element instanceof PsiMethod || element instanceof PsiClass;
@@ -240,7 +247,7 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler {
}
}
return null;
return getCannotInlineDueToUsagesMessage(psiClass);
}
static boolean isRedundantImplements(final PsiClass superClass, final PsiClassType interfaceType) {
@@ -266,6 +273,53 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler {
return stmt.get();
}
@Nullable
private static String getCannotInlineDueToUsagesMessage(final PsiClass aClass) {
boolean hasUsages = false;
for(PsiReference reference : ReferencesSearch.search(aClass)) {
final PsiElement element = reference.getElement();
if (element == null) continue;
if (!PsiTreeUtil.isAncestor(aClass, element, false)) {
hasUsages = true;
}
final PsiElement parentElement = element.getParent();
if (parentElement != null) {
if (parentElement.getParent() instanceof PsiClassObjectAccessExpression) {
return "Class cannot be inlined because it has usages of its class literal";
}
if (ourCatchClausePattern.accepts(parentElement)) {
return "Class cannot be inlined because it is used in a 'catch' clause";
}
}
if (ourThrowsClausePattern.accepts(element)) {
return "Class cannot be inlined because it is used in a 'throws' clause";
}
if (parentElement instanceof PsiThisExpression) {
return "Class cannot be inlined because it is used as a 'this' qualifier";
}
if (parentElement instanceof PsiNewExpression) {
final PsiNewExpression newExpression = (PsiNewExpression)parentElement;
final PsiMethod[] constructors = aClass.getConstructors();
if (constructors.length == 0) {
PsiExpressionList newArgumentList = newExpression.getArgumentList();
if (newArgumentList != null && newArgumentList.getExpressions().length > 0) {
return "Class cannot be inlined because a call to its constructor is unresolved";
}
}
else {
final JavaResolveResult resolveResult = newExpression.resolveMethodGenerics();
if (!resolveResult.isValidResult()) {
return "Class cannot be inlined because a call to its constructor is unresolved";
}
}
}
}
if (!hasUsages) {
return RefactoringBundle.message("class.is.never.used");
}
return null;
}
private static class AllowedUsagesProcessor implements Processor<PsiReference> {
private final PsiElement myPsiElement;
@@ -19,8 +19,6 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
@@ -29,7 +27,6 @@ import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.rename.NonCodeUsageInfoFactory;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.TextOccurrencesUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
@@ -52,11 +49,6 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor {
private final boolean mySearchInComments;
private final boolean mySearchInNonJavaFiles;
private final ElementPattern ourCatchClausePattern = PlatformPatterns.psiElement(PsiTypeElement.class).withParent(PlatformPatterns.psiElement(PsiParameter.class).withParent(
PlatformPatterns.psiElement(PsiCatchSection.class)));
private final ElementPattern ourThrowsClausePattern = PlatformPatterns.psiElement().withParent(PlatformPatterns.psiElement(PsiReferenceList.class).withFirstChild(
PlatformPatterns.psiElement().withText(PsiKeyword.THROWS)));
protected InlineToAnonymousClassProcessor(Project project,
PsiClass psiClass,
@Nullable final PsiCall callToInline,
@@ -134,13 +126,7 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor {
}
protected boolean preprocessUsages(final Ref<UsageInfo[]> refUsages) {
UsageInfo[] usages = refUsages.get();
String s = getPreprocessUsagesMessage(usages);
if (s != null) {
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("inline.to.anonymous.refactoring"), s, null, myClass.getProject());
return false;
}
MultiMap<PsiElement, String> conflicts = getConflicts(usages);
MultiMap<PsiElement, String> conflicts = getConflicts(refUsages.get());
if (!conflicts.isEmpty()) {
return showConflicts(conflicts);
}
@@ -280,51 +266,4 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor {
return RefactoringBundle.message("inline.to.anonymous.command.name", myClass.getQualifiedName());
}
@Nullable
public String getPreprocessUsagesMessage(final UsageInfo[] usages) {
boolean hasUsages = false;
for(UsageInfo usage: usages) {
final PsiElement element = usage.getElement();
if (element == null) continue;
if (!PsiTreeUtil.isAncestor(myClass, element, false)) {
hasUsages = true;
}
final PsiElement parentElement = element.getParent();
if (parentElement != null) {
if (parentElement.getParent() instanceof PsiClassObjectAccessExpression) {
return "Class cannot be inlined because it has usages of its class literal";
}
if (ourCatchClausePattern.accepts(parentElement)) {
return "Class cannot be inlined because it is used in a 'catch' clause";
}
}
if (ourThrowsClausePattern.accepts(element)) {
return "Class cannot be inlined because it is used in a 'throws' clause";
}
if (parentElement instanceof PsiThisExpression) {
return "Class cannot be inlined because it is used as a 'this' qualifier";
}
if (parentElement instanceof PsiNewExpression) {
final PsiNewExpression newExpression = (PsiNewExpression)parentElement;
final PsiMethod[] constructors = myClass.getConstructors();
if (constructors.length == 0) {
PsiExpressionList newArgumentList = newExpression.getArgumentList();
if (newArgumentList != null && newArgumentList.getExpressions().length > 0) {
return "Class cannot be inlined because a call to its constructor is unresolved";
}
}
else {
final JavaResolveResult resolveResult = newExpression.resolveMethodGenerics();
if (!resolveResult.isValidResult()) {
return "Class cannot be inlined because a call to its constructor is unresolved";
}
}
}
}
if (!hasUsages) {
return RefactoringBundle.message("class.is.never.used");
}
return null;
}
}
@@ -1,9 +0,0 @@
import java.lang.Exception;
public class Simple extends Exception{}
class Usage {
void foo() throws Simple {
throw new Si<caret>mple();
}
}
@@ -348,9 +348,12 @@ public class InlineToAnonymousClassTest extends LightCodeInsightTestCase {
}
private void doTestPreprocessUsages(final String expectedMessage) throws Exception {
final InlineToAnonymousClassProcessor processor = prepareProcessor();
String message = processor.getPreprocessUsagesMessage(processor.findUsages());
assertEquals(expectedMessage, message);
configureByFile("/refactoring/inlineToAnonymousClass/" + getTestName(false) + ".java");
PsiElement element = TargetElementUtilBase.findTargetElement(myEditor, TargetElementUtilBase
.ELEMENT_NAME_ACCEPTED | TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED);
assertInstanceOf(element, PsiClass.class);
final PsiClass psiClass = (PsiClass)element;
assertEquals(expectedMessage, InlineToAnonymousClassHandler.getCannotInlineMessage(psiClass));
}
private InlineToAnonymousClassProcessor prepareProcessor() throws Exception {
@@ -415,10 +418,6 @@ public class InlineToAnonymousClassTest extends LightCodeInsightTestCase {
doTestCanBeInvokedOnReference(true);
}
public void testCantBeInvokedOnReferenceThrowStatement() throws Exception {
doTestCanBeInvokedOnReference(false);
}
private void doTestCanBeInvokedOnReference(boolean canBeInvokedOnReference) throws Exception {
configureByFile("/refactoring/inlineToAnonymousClass/" + getTestName(false) + ".java");
PsiElement element = TargetElementUtilBase