Inline inner: check when possible to inline this ref only (IDEA-39274)

This commit is contained in:
anna
2010-01-29 17:07:23 +03:00
parent 22353e8a58
commit de02933e05
14 changed files with 220 additions and 18 deletions
@@ -15,14 +15,14 @@
*/
package com.intellij.refactoring.inline;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.help.HelpManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiCall;
import com.intellij.psi.PsiClass;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.JavaRefactoringSettings;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.JavaRefactoringSettings;
import com.intellij.refactoring.RefactoringBundle;
import javax.swing.*;
import java.awt.*;
@@ -36,11 +36,11 @@ public class InlineToAnonymousClassDialog extends InlineOptionsDialog {
private JCheckBox myCbSearchInComments;
private JCheckBox myCbSearchTextOccurences;
protected InlineToAnonymousClassDialog(Project project, PsiClass psiClass, final PsiCall callToInline) {
protected InlineToAnonymousClassDialog(Project project, PsiClass psiClass, final PsiCall callToInline, boolean isInvokeOnReference) {
super(project, true, psiClass);
myClass = psiClass;
myCallToInline = callToInline;
myInvokedOnReference = (myCallToInline != null);
myInvokedOnReference = isInvokeOnReference;
setTitle(RefactoringBundle.message("inline.to.anonymous.refactoring"));
init();
}
@@ -69,10 +69,6 @@ public class InlineToAnonymousClassDialog extends InlineOptionsDialog {
protected JComponent createCenterPanel() {
JComponent optionsPanel = super.createCenterPanel();
// TODO[yole]: make visible again when "inline this" option is fixed (IDEADEV-17928)
myOptionsPanel.setVisible(false);
myRbInlineAll.setSelected(true);
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
@@ -24,9 +24,11 @@ import com.intellij.psi.*;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Processor;
import org.jetbrains.annotations.Nullable;
@@ -62,16 +64,68 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler {
final PsiClass psiClass = psiElement instanceof PsiMethod ? ((PsiMethod) psiElement).getContainingClass() : (PsiClass) psiElement;
PsiCall callToInline = findCallToInline(editor);
final PsiClassType superType = InlineToAnonymousClassProcessor.getSuperType(psiClass);
if (superType == null) {
CommonRefactoringUtil.showErrorHint(project, editor, "java.lang.Object is not found", RefactoringBundle.message("inline.to.anonymous.refactoring"), null);
return;
}
String errorMessage = getCannotInlineMessage(psiClass);
if (errorMessage != null) {
CommonRefactoringUtil.showErrorHint(project, editor, errorMessage, RefactoringBundle.message("inline.to.anonymous.refactoring"), null);
return;
}
InlineToAnonymousClassDialog dlg = new InlineToAnonymousClassDialog(project, psiClass, callToInline);
dlg.show();
new InlineToAnonymousClassDialog(project, psiClass, callToInline, canBeInvokedOnReference(callToInline, superType)).show();
}
public static boolean canBeInvokedOnReference(PsiCall callToInline, PsiType superType) {
if (callToInline != null) {
final PsiElement parent = callToInline.getParent();
if (parent instanceof PsiExpressionStatement || parent instanceof PsiSynchronizedStatement) {
return true;
}
else if (parent instanceof PsiReferenceExpression) {
return true;
}
else if (parent instanceof PsiExpressionList) {
final PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(parent, PsiMethodCallExpression.class);
if (methodCallExpression != null) {
int paramIdx = ArrayUtil.find(methodCallExpression.getArgumentList().getExpressions(), callToInline);
if (paramIdx != -1) {
final JavaResolveResult resolveResult = methodCallExpression.resolveMethodGenerics();
final PsiElement resolvedMethod = resolveResult.getElement();
if (resolvedMethod instanceof PsiMethod) {
PsiType paramType;
final PsiParameter[] parameters = ((PsiMethod)resolvedMethod).getParameterList().getParameters();
if (paramIdx >= parameters.length) {
final PsiParameter varargParameter = parameters[parameters.length - 1];
paramType = varargParameter.getType();
}
else {
paramType = parameters[paramIdx].getType();
}
if (paramType instanceof PsiEllipsisType) {
paramType = ((PsiEllipsisType)paramType).getComponentType();
}
paramType = resolveResult.getSubstitutor().substitute(paramType);
final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)callToInline).getClassOrAnonymousClassReference();
if (classReference != null) {
superType = classReference.advancedResolve(false).getSubstitutor().substitute(superType);
if (TypeConversionUtil.isAssignable(paramType, superType)) {
return true;
}
}
}
}
}
}
}
return false;
}
@Nullable
public static PsiCall findCallToInline(final Editor editor) {
PsiCall callToInline = null;
@@ -168,8 +168,8 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor {
}
protected void performRefactoring(UsageInfo[] usages) {
PsiClassType superType = getSuperType();
final PsiClassType superType = getSuperType(myClass);
LOG.assertTrue(superType != null);
List<PsiElement> elementsToDelete = new ArrayList<PsiElement>();
List<PsiNewExpression> newExpressions = new ArrayList<PsiNewExpression>();
for(UsageInfo info: usages) {
@@ -249,22 +249,27 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor {
}
}
private PsiClassType getSuperType() {
PsiElementFactory factory = JavaPsiFacade.getInstance(myClass.getProject()).getElementFactory();
@Nullable
public static PsiClassType getSuperType(final PsiClass aClass) {
PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory();
PsiClassType superType;
PsiClass superClass = myClass.getSuperClass();
PsiClassType[] interfaceTypes = myClass.getImplementsListTypes();
PsiClass superClass = aClass.getSuperClass();
PsiClassType[] interfaceTypes = aClass.getImplementsListTypes();
if (interfaceTypes.length > 0 && !InlineToAnonymousClassHandler.isRedundantImplements(superClass, interfaceTypes [0])) {
assert interfaceTypes.length == 1;
superType = interfaceTypes [0];
}
else {
PsiClassType[] classTypes = myClass.getExtendsListTypes();
PsiClassType[] classTypes = aClass.getExtendsListTypes();
if (classTypes.length > 0) {
superType = classTypes [0];
}
else {
if (superClass == null) {
//java.lang.Object was not found
return null;
}
superType = factory.createType(superClass);
}
}
@@ -0,0 +1,7 @@
public class Simple {}
class Usage {
void foo() {
String s = new Si<caret>mple().toString();
}
}
@@ -0,0 +1,11 @@
public class Simple implements Runnable {
public void run(){}
}
class Usage {
void foo() {
bar(new Si<caret>mple());
}
void bar(Runnable r){}
}
@@ -0,0 +1,11 @@
public class Simple<T> implements Comparable<T> {
public int compareTo(T o){}
}
class Usage {
void foo() {
bar(new Si<caret>mple<String>());
}
void bar(Comparable<String>... r){}
}
@@ -0,0 +1,11 @@
public class Simple<T> implements Comparable<T> {
public int compareTo(T o){}
}
class Usage<S> {
void foo() {
bar(new Si<caret>mple<S>());
}
void bar(Comparable<S>... r){}
}
@@ -0,0 +1,9 @@
public class Simple {}
class Usage {
void foo() {
synchronized (new Si<caret>mple()) {
//dosmth
}
}
}
@@ -0,0 +1,11 @@
public class Simple implements Runnable {
public void run(){}
}
class Usage {
void foo() {
bar(new Si<caret>mple());
}
void bar(Runnable... r){}
}
@@ -0,0 +1,7 @@
public class Simple {}
class Usage {
void foo() {
Simple s = new Si<caret>mple();
}
}
@@ -0,0 +1,11 @@
public class Simple implements Runnable {
public void run(){}
}
class Usage {
void foo() {
bar(new Si<caret>mple());
}
void bar(Simple s){}
}
@@ -0,0 +1,7 @@
public class Simple {}
class Usage {
Simple foo() {
return new Si<caret>mple();
}
}
@@ -0,0 +1,9 @@
import java.lang.Exception;
public class Simple extends Exception{}
class Usage {
void foo() throws Simple {
throw new Si<caret>mple();
}
}
@@ -6,6 +6,7 @@ import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.JavaSdkImpl;
import com.intellij.psi.PsiCall;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiElement;
import com.intellij.testFramework.LightCodeInsightTestCase;
import com.intellij.usageView.UsageInfo;
@@ -377,4 +378,56 @@ public class InlineToAnonymousClassTest extends LightCodeInsightTestCase {
assertEquals(0, conflicts.size());
processor.run();
}
public void testCanBeInvokedOnReference() throws Exception {
doTestCanBeInvokedOnReference(true);
}
public void testCanBeInvokedOnReference1() throws Exception {
doTestCanBeInvokedOnReference(true);
}
public void testCanBeInvokedOnReferenceSubstitution() throws Exception {
doTestCanBeInvokedOnReference(true);
}
public void testCanBeInvokedOnReferenceSubstitution1() throws Exception {
doTestCanBeInvokedOnReference(true);
}
public void testCanBeInvokedOnReferenceVarargs() throws Exception {
doTestCanBeInvokedOnReference(true);
}
public void testCantBeInvokedOnReference() throws Exception {
doTestCanBeInvokedOnReference(false);
}
public void testCantBeInvokedOnReference1() throws Exception {
doTestCanBeInvokedOnReference(false);
}
public void testCantBeInvokedOnReferenceReturnStatement() throws Exception {
doTestCanBeInvokedOnReference(false);
}
public void testCanBeInvokedOnReferenceSyncStatement() throws Exception {
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
.findTargetElement(myEditor, TargetElementUtilBase.ELEMENT_NAME_ACCEPTED | TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED);
PsiCall callToInline = InlineToAnonymousClassHandler.findCallToInline(myEditor);
PsiClass classToInline = (PsiClass) element;
assertEquals(null, InlineToAnonymousClassHandler.getCannotInlineMessage(classToInline));
final PsiClassType superType = InlineToAnonymousClassProcessor.getSuperType(classToInline);
assertTrue(superType != null);
assertEquals(canBeInvokedOnReference, InlineToAnonymousClassHandler.canBeInvokedOnReference(callToInline, superType));
}
}