mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-151190 Change class signature doesn't support complicated bounds
This commit is contained in:
+3
-14
@@ -24,7 +24,6 @@ import com.intellij.refactoring.changeClassSignature.ChangeClassSignatureDialog;
|
||||
import com.intellij.refactoring.changeClassSignature.TypeParameterInfo;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -123,26 +122,16 @@ public class ChangeClassSignatureFromUsageFix extends BaseIntentionAction {
|
||||
else {
|
||||
suggestedName = suggester.suggestUnusedName("T");
|
||||
}
|
||||
final PsiTypeCodeFragment boundFragment = createBoundCodeFragment(boundType, typeElement, factory);
|
||||
final PsiTypeCodeFragment boundFragment = ChangeClassSignatureDialog.createTableCodeFragment(boundType, typeElement, factory, true);
|
||||
result.add(new TypeParameterInfoView(new TypeParameterInfo.New(suggestedName, defaultType, null),
|
||||
boundFragment,
|
||||
boundType == null ? factory.createTypeCodeFragment(suggestedName, typeElement, true)
|
||||
: createBoundCodeFragment(boundType, typeElement, factory)));
|
||||
: ChangeClassSignatureDialog
|
||||
.createTableCodeFragment(boundType, typeElement, factory, false)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static PsiTypeCodeFragment createBoundCodeFragment(@Nullable PsiClassType boundType,
|
||||
@NotNull PsiElement context,
|
||||
@NotNull JavaCodeFragmentFactory factory) {
|
||||
final PsiTypeCodeFragment boundFragment =
|
||||
factory.createTypeCodeFragment(boundType == null ? "" : boundType.getClassName(), context, true);
|
||||
if (boundType != null) {
|
||||
boundFragment.addImportsFromString(boundType.getCanonicalText());
|
||||
}
|
||||
return boundFragment;
|
||||
}
|
||||
|
||||
private static boolean isAssignable(@NotNull PsiTypeParameter typeParameter, @NotNull PsiType type) {
|
||||
for (PsiClassType t : typeParameter.getExtendsListTypes()) {
|
||||
if (!t.isAssignableFrom(type)) {
|
||||
|
||||
@@ -207,7 +207,10 @@ public class PsiCodeFragmentImpl extends PsiFileImpl implements JavaCodeFragment
|
||||
}
|
||||
|
||||
IElementType i = myContentElementType;
|
||||
if (i == JavaElementType.TYPE_TEXT || i == JavaElementType.EXPRESSION_STATEMENT || i == JavaElementType.REFERENCE_TEXT) {
|
||||
if (i == JavaElementType.TYPE_WITH_CONJUNCTIONS_TEXT ||
|
||||
i == JavaElementType.TYPE_WITH_DISJUNCTIONS_TEXT ||
|
||||
i == JavaElementType.EXPRESSION_STATEMENT ||
|
||||
i == JavaElementType.REFERENCE_TEXT) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.source;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
@@ -29,8 +30,11 @@ import static com.intellij.util.BitUtil.isSet;
|
||||
* @author dsl
|
||||
*/
|
||||
public class PsiTypeCodeFragmentImpl extends PsiCodeFragmentImpl implements PsiTypeCodeFragment {
|
||||
private final static Logger LOG = Logger.getInstance(PsiTypeCodeFragmentImpl.class);
|
||||
|
||||
private final boolean myAllowEllipsis;
|
||||
private final boolean myAllowDisjunction;
|
||||
private final boolean myAllowConjunction;
|
||||
|
||||
public PsiTypeCodeFragmentImpl(final Project project,
|
||||
final boolean isPhysical,
|
||||
@@ -38,10 +42,17 @@ public class PsiTypeCodeFragmentImpl extends PsiCodeFragmentImpl implements PsiT
|
||||
final CharSequence text,
|
||||
final int flags,
|
||||
PsiElement context) {
|
||||
super(project, JavaElementType.TYPE_TEXT, isPhysical, name, text, context);
|
||||
super(project,
|
||||
isSet(flags, JavaCodeFragmentFactory.ALLOW_INTERSECTION) ? JavaElementType.TYPE_WITH_CONJUNCTIONS_TEXT : JavaElementType.TYPE_WITH_DISJUNCTIONS_TEXT,
|
||||
isPhysical,
|
||||
name,
|
||||
text,
|
||||
context);
|
||||
|
||||
myAllowEllipsis = isSet(flags, JavaCodeFragmentFactory.ALLOW_ELLIPSIS);
|
||||
myAllowDisjunction = isSet(flags, JavaCodeFragmentFactory.ALLOW_DISJUNCTION);
|
||||
myAllowConjunction = isSet(flags, JavaCodeFragmentFactory.ALLOW_INTERSECTION);
|
||||
LOG.assertTrue(!myAllowConjunction || !myAllowDisjunction);
|
||||
|
||||
if (isSet(flags, JavaCodeFragmentFactory.ALLOW_VOID)) {
|
||||
putUserData(PsiUtil.VALID_VOID_TYPE_IN_CODE_FRAGMENT, Boolean.TRUE);
|
||||
@@ -79,6 +90,9 @@ public class PsiTypeCodeFragmentImpl extends PsiCodeFragmentImpl implements PsiT
|
||||
else if (type instanceof PsiDisjunctionType && !myAllowDisjunction) {
|
||||
throw new TypeSyntaxException("Disjunction not allowed: " + type);
|
||||
}
|
||||
else if (type instanceof PsiDisjunctionType && !myAllowConjunction) {
|
||||
throw new TypeSyntaxException("Conjunction not allowed: " + type);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
+15
-7
@@ -20,6 +20,7 @@ import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.HelpID;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.ui.CodeFragmentTableCellRenderer;
|
||||
@@ -109,11 +110,6 @@ public class ChangeClassSignatureDialog extends RefactoringDialog {
|
||||
init();
|
||||
}
|
||||
|
||||
private PsiTypeCodeFragment createValueCodeFragment() {
|
||||
final JavaCodeFragmentFactory factory = JavaCodeFragmentFactory.getInstance(myProject);
|
||||
return factory.createTypeCodeFragment("", myClass.getLBrace(), true);
|
||||
}
|
||||
|
||||
protected JComponent createNorthPanel() {
|
||||
return new JLabel(RefactoringBundle.message("changeClassSignature.class.label.text", DescriptiveNameUtil.getDescriptiveName(myClass)));
|
||||
}
|
||||
@@ -240,6 +236,16 @@ public class ChangeClassSignatureDialog extends RefactoringDialog {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PsiTypeCodeFragment createTableCodeFragment(@Nullable PsiClassType type,
|
||||
@NotNull PsiElement context,
|
||||
@NotNull JavaCodeFragmentFactory factory,
|
||||
boolean allowConjunctions) {
|
||||
return factory.createTypeCodeFragment(type == null ? "" : type.getCanonicalText(),
|
||||
context,
|
||||
true,
|
||||
(allowConjunctions && PsiUtil.isLanguageLevel8OrHigher(context)) ? JavaCodeFragmentFactory.ALLOW_INTERSECTION : 0);
|
||||
}
|
||||
|
||||
private class MyTableModel extends AbstractTableModel implements EditableModel {
|
||||
public int getColumnCount() {
|
||||
return 3;
|
||||
@@ -301,8 +307,10 @@ public class ChangeClassSignatureDialog extends RefactoringDialog {
|
||||
public void addRow() {
|
||||
TableUtil.stopEditing(myTable);
|
||||
myTypeParameterInfos.add(new TypeParameterInfo.New("", null, null));
|
||||
myBoundValueTypeCodeFragments.add(createValueCodeFragment());
|
||||
myDefaultValueTypeCodeFragments.add(createValueCodeFragment());
|
||||
JavaCodeFragmentFactory codeFragmentFactory = JavaCodeFragmentFactory.getInstance(myProject);
|
||||
PsiElement context = myClass.getLBrace() != null ? myClass.getLBrace() : myClass;
|
||||
myBoundValueTypeCodeFragments.add(createTableCodeFragment(null, context, codeFragmentFactory, true));
|
||||
myDefaultValueTypeCodeFragments.add(createTableCodeFragment(null, context, codeFragmentFactory, false));
|
||||
final int row = myDefaultValueTypeCodeFragments.size() - 1;
|
||||
fireTableRowsInserted(row, row);
|
||||
}
|
||||
|
||||
+3
-1
@@ -185,7 +185,9 @@ public class ChangeClassSignatureProcessor extends BaseRefactoringProcessor {
|
||||
for (final TypeParameterInfo info : myNewSignature) {
|
||||
newTypeParameters.add(info.getTypeParameter(originalTypeParameters, myProject));
|
||||
}
|
||||
ChangeSignatureUtil.synchronizeList(myClass.getTypeParameterList(), newTypeParameters, TypeParameterList.INSTANCE, toRemoveParms);
|
||||
final PsiTypeParameterList parameterList = myClass.getTypeParameterList();
|
||||
ChangeSignatureUtil.synchronizeList(parameterList, newTypeParameters, TypeParameterList.INSTANCE, toRemoveParms);
|
||||
JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(parameterList);
|
||||
}
|
||||
|
||||
private boolean[] detectRemovedParameters(final PsiTypeParameter[] original) {
|
||||
|
||||
+2
@@ -22,6 +22,7 @@ import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
/**
|
||||
* @author dsl
|
||||
@@ -42,6 +43,7 @@ public interface TypeParameterInfo {
|
||||
myBoundValue = boundValue != null ? CanonicalTypes.createTypeWrapper(boundValue) : null;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public New(@NotNull PsiClass aClass,
|
||||
@NotNull @NonNls String name,
|
||||
@NotNull @NonNls String defaultValue,
|
||||
|
||||
@@ -67,6 +67,10 @@ public abstract class JavaCodeFragmentFactory {
|
||||
* Flag for {@linkplain #createTypeCodeFragment(String, PsiElement, boolean, int)} - allows disjunctive type.
|
||||
*/
|
||||
public static final int ALLOW_DISJUNCTION = 0x04;
|
||||
/**
|
||||
* Flag for {@linkplain #createTypeCodeFragment(String, PsiElement, boolean, int)} - allows conjunctive type.
|
||||
*/
|
||||
public static final int ALLOW_INTERSECTION = 0x08;
|
||||
|
||||
/**
|
||||
* Creates a Java type code fragment from the text of the name of a Java type (the name
|
||||
@@ -98,7 +102,7 @@ public abstract class JavaCodeFragmentFactory {
|
||||
public abstract PsiTypeCodeFragment createTypeCodeFragment(@NotNull String text,
|
||||
@Nullable PsiElement context,
|
||||
boolean isPhysical,
|
||||
@MagicConstant(flags = {ALLOW_VOID, ALLOW_ELLIPSIS, ALLOW_DISJUNCTION}) int flags);
|
||||
@MagicConstant(flags = {ALLOW_VOID, ALLOW_ELLIPSIS, ALLOW_DISJUNCTION, ALLOW_INTERSECTION}) int flags);
|
||||
|
||||
/**
|
||||
* Creates a Java reference code fragment from the text of a Java reference to a
|
||||
|
||||
@@ -127,8 +127,13 @@ public class PsiIntersectionType extends PsiType.Stub {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getCanonicalText(boolean annotated) {
|
||||
return myConjuncts[0].getCanonicalText(annotated);
|
||||
public String getCanonicalText(final boolean annotated) {
|
||||
return StringUtil.join(myConjuncts, new Function<PsiType, String>() {
|
||||
@Override
|
||||
public String fun(PsiType psiType) {
|
||||
return psiType.getCanonicalText(annotated);
|
||||
}
|
||||
}, " & ");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -240,12 +240,24 @@ public interface JavaElementType {
|
||||
}
|
||||
};
|
||||
|
||||
IElementType TYPE_TEXT = new ICodeFragmentElementType("TYPE_TEXT", JavaLanguage.INSTANCE) {
|
||||
IElementType TYPE_WITH_DISJUNCTIONS_TEXT = new TypeTextElementType("TYPE_WITH_DISJUNCTIONS_TEXT", ReferenceParser.DISJUNCTIONS);
|
||||
IElementType TYPE_WITH_CONJUNCTIONS_TEXT = new TypeTextElementType("TYPE_WITH_CONJUNCTIONS_TEXT", ReferenceParser.CONJUNCTIONS);
|
||||
|
||||
class TypeTextElementType extends ICodeFragmentElementType {
|
||||
private final int myFlags;
|
||||
|
||||
public TypeTextElementType(@NonNls String debugName, int flags) {
|
||||
super(debugName, JavaLanguage.INSTANCE);
|
||||
myFlags = flags;
|
||||
}
|
||||
|
||||
private final JavaParserUtil.ParserWrapper myParser = new JavaParserUtil.ParserWrapper() {
|
||||
@Override
|
||||
public void parse(final PsiBuilder builder) {
|
||||
JavaParser.INSTANCE.getReferenceParser().parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.ELLIPSIS |
|
||||
ReferenceParser.WILDCARD | ReferenceParser.DISJUNCTIONS);
|
||||
JavaParser.INSTANCE.getReferenceParser().parseType(builder, ReferenceParser.EAT_LAST_DOT |
|
||||
ReferenceParser.ELLIPSIS |
|
||||
ReferenceParser.WILDCARD |
|
||||
myFlags);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -254,7 +266,7 @@ public interface JavaElementType {
|
||||
public ASTNode parseContents(final ASTNode chameleon) {
|
||||
return JavaParserUtil.parseFragment(chameleon, myParser);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class JavaDummyElementType extends ILazyParseableElementType implements ICompositeElementType {
|
||||
private JavaDummyElementType() {
|
||||
|
||||
@@ -246,11 +246,13 @@ public class CanonicalTypes {
|
||||
}
|
||||
}
|
||||
|
||||
private static class DisjunctionType extends Type {
|
||||
private static class LogicalOperationType extends Type {
|
||||
private final List<Type> myTypes;
|
||||
private final boolean myDisjunction;
|
||||
|
||||
private DisjunctionType(List<Type> types) {
|
||||
private LogicalOperationType(List<Type> types, boolean disjunction) {
|
||||
myTypes = types;
|
||||
myDisjunction = disjunction;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -262,7 +264,7 @@ public class CanonicalTypes {
|
||||
return type.getType(context, manager);
|
||||
}
|
||||
});
|
||||
return new PsiDisjunctionType(types, manager);
|
||||
return myDisjunction ? new PsiDisjunctionType(types, manager) : PsiIntersectionType.createIntersection(types);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -272,7 +274,7 @@ public class CanonicalTypes {
|
||||
public String fun(Type type) {
|
||||
return type.getTypeText();
|
||||
}
|
||||
}, "|");
|
||||
}, myDisjunction ? "|" : "&");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -337,7 +339,19 @@ public class CanonicalTypes {
|
||||
return type.accept(Creator.this);
|
||||
}
|
||||
});
|
||||
return new DisjunctionType(types);
|
||||
return new LogicalOperationType(types, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Type visitIntersectionType(PsiIntersectionType type) {
|
||||
List<Type> types = ContainerUtil.map(type.getConjuncts(), new Function<PsiType, Type>() {
|
||||
@Override
|
||||
public Type fun(PsiType type) {
|
||||
return type.accept(Creator.this);
|
||||
}
|
||||
});
|
||||
return new LogicalOperationType(types, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import java.io.Serializable;
|
||||
|
||||
class <caret>C {}
|
||||
|
||||
class Some implements Runnable, Serializable {
|
||||
void m() {
|
||||
C c = new C();
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import java.io.Serializable;
|
||||
|
||||
class C<T extends Runnable & Serializable> {}
|
||||
|
||||
class Some implements Runnable, Serializable {
|
||||
void m() {
|
||||
C<Some> c = new C<Some>();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
|
||||
class B<T extends java.util.Collection> {}
|
||||
class B<T extends Collection> {}
|
||||
|
||||
public void someMethod() {
|
||||
B<List> b = new B<List>();
|
||||
|
||||
+2
-1
@@ -1,8 +1,9 @@
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
|
||||
class B<T extends java.util.Collection, X extends Long> {}
|
||||
class B<T extends Collection, X extends Long> {}
|
||||
|
||||
public void someMethod() {
|
||||
B<List, Long> b = new B<>();
|
||||
|
||||
+14
-2
@@ -2,8 +2,7 @@ package com.intellij.refactoring.changeClassSignature;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInsight.TargetElementUtil;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.refactoring.LightRefactoringTestCase;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -100,6 +99,19 @@ public class ChangeClassSignatureTest extends LightRefactoringTestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public void testAddBoundWithIntersection() throws Exception {
|
||||
doTest(aClass -> {
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(aClass.getProject());
|
||||
final PsiFile context = aClass.getContainingFile();
|
||||
return new TypeParameterInfo[]{
|
||||
new TypeParameterInfo.New("T",
|
||||
factory.createTypeFromText("Some", context),
|
||||
PsiIntersectionType.createIntersection(factory.createTypeFromText("java.lang.Runnable", context),
|
||||
factory.createTypeFromText("java.io.Serializable", context)))
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void doTest(Function<PsiClass, TypeParameterInfo[]> gen) throws Exception {
|
||||
@NonNls final String filePathBefore = getTestName(false) + ".java";
|
||||
@NonNls final String filePathAfter = getTestName(false) + ".java.after";
|
||||
|
||||
Reference in New Issue
Block a user