More fair handle of PsiDisjunctionType; introduce variable for multi-catch types

This commit is contained in:
Roman Shevchenko
2011-02-17 17:52:21 +01:00
parent b417cb4606
commit 6b44b057fa
12 changed files with 171 additions and 63 deletions
@@ -13,10 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @author max
*/
package com.intellij.psi.impl.smartPointers;
import com.intellij.openapi.diagnostic.Logger;
@@ -26,20 +22,28 @@ import com.intellij.psi.impl.PsiSubstitutorImpl;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.NullableFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author max
*/
public class SmartTypePointerManagerImpl extends SmartTypePointerManager {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.SmartTypePointerManagerImpl");
private final SmartPointerManager myPsiPointerManager;
private final Project myProject;
public SmartTypePointerManagerImpl(SmartPointerManager psiPointerManager, final Project project) {
public SmartTypePointerManagerImpl(final SmartPointerManager psiPointerManager, final Project project) {
myPsiPointerManager = psiPointerManager;
myProject = project;
}
@@ -102,26 +106,24 @@ public class SmartTypePointerManagerImpl extends SmartTypePointerManager {
return PsiWildcardType.createUnbounded(myManager);
}
else {
final PsiType type = myBoundPointer.getType();
assert type != null : myBoundPointer;
if (myIsExtending) {
return PsiWildcardType.createExtends(myManager, myBoundPointer.getType());
return PsiWildcardType.createExtends(myManager, type);
}
else {
return PsiWildcardType.createSuper(myManager, myBoundPointer.getType());
return PsiWildcardType.createSuper(myManager, type);
}
}
}
}
private static class ClassTypePointer implements SmartTypePointer {
private PsiType myType;
private final SmartPsiElementPointer myClass;
private final Map<SmartPsiElementPointer, SmartTypePointer> myMap;
public ClassTypePointer(PsiType type,
SmartPsiElementPointer aClass,
Map<SmartPsiElementPointer, SmartTypePointer> map) {
public ClassTypePointer(PsiType type, SmartPsiElementPointer aClass, Map<SmartPsiElementPointer, SmartTypePointer> map) {
myType = type;
myClass = aClass;
myMap = map;
@@ -182,15 +184,40 @@ public class SmartTypePointerManagerImpl extends SmartTypePointerManager {
}
}
private class DisjunctionTypePointer implements SmartTypePointer {
private PsiType myType;
private final List<SmartTypePointer> myPointers;
private DisjunctionTypePointer(final PsiDisjunctionType type) {
myType = type;
myPointers = ContainerUtil.map(type.getDisjunctions(), new Function<PsiType, SmartTypePointer>() {
@Override public SmartTypePointer fun(PsiType psiType) { return createSmartTypePointer(psiType); }
});
}
@Override
public PsiType getType() {
if (myType.isValid()) return myType;
final List<PsiType> types = ContainerUtil.map(myPointers, new NullableFunction<SmartTypePointer, PsiType>() {
@Override public PsiType fun(SmartTypePointer typePointer) { return typePointer.getType(); }
});
return new PsiDisjunctionType(types, PsiManager.getInstance(myProject));
}
}
private class SmartTypeCreatingVisitor extends PsiTypeVisitor<SmartTypePointer> {
@Override
public SmartTypePointer visitPrimitiveType(PsiPrimitiveType primitiveType) {
return new SimpleTypePointer(primitiveType);
}
@Override
public SmartTypePointer visitArrayType(PsiArrayType arrayType) {
return new ArrayTypePointer(arrayType, arrayType.getComponentType().accept(this));
}
@Override
public SmartTypePointer visitWildcardType(PsiWildcardType wildcardType) {
final PsiType bound = wildcardType.getBound();
final SmartTypePointer boundPointer;
@@ -203,6 +230,7 @@ public class SmartTypePointerManagerImpl extends SmartTypePointerManager {
return new WildcardTypePointer(wildcardType, boundPointer);
}
@Override
public SmartTypePointer visitClassType(PsiClassType classType) {
final PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics();
final PsiClass aClass = resolveResult.getElement();
@@ -226,6 +254,10 @@ public class SmartTypePointerManagerImpl extends SmartTypePointerManager {
}
return new ClassTypePointer(classType, myPsiPointerManager.createSmartPsiElementPointer(aClass), map);
}
}
@Override
public SmartTypePointer visitDisjunctionType(PsiDisjunctionType disjunctionType) {
return new DisjunctionTypePointer(disjunctionType);
}
}
}
@@ -32,6 +32,7 @@ import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PatchedSoftReference;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -102,7 +103,12 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
cachedType = componentType.createArrayType();
}
else {
cachedType = new PsiDisjunctionType(this);
final List<PsiTypeElement> typeElements = PsiTreeUtil.getChildrenOfTypeAsList(this, PsiTypeElement.class);
if (typeElements.size() < 2) LOG.error("Incorrect nested type: " + this);
final List<PsiType> types = ContainerUtil.map(typeElements, new Function<PsiTypeElement, PsiType>() {
@Override public PsiType fun(final PsiTypeElement psiTypeElement) { return psiTypeElement.getType(); }
});
cachedType = new PsiDisjunctionType(types, getManager());
}
}
else if (elementType == JavaElementType.JAVA_CODE_REFERENCE) {
@@ -180,15 +180,18 @@ public class JavaChangeUtilSupport implements TreeGenerator, TreeCopyHandler {
return createType(original.getProject(), originalText, null, generated);
}
if (type instanceof PsiIntersectionType) {
PsiIntersectionType intersectionType = (PsiIntersectionType)type;
LightTypeElement te = new LightTypeElement(original.getManager(), intersectionType.getConjuncts()[0]);
LightTypeElement te = new LightTypeElement(original.getManager(), ((PsiIntersectionType)type).getRepresentative());
return ChangeUtil.generateTreeElement(te, table, manager);
}
if (type instanceof PsiDisjunctionType) {
LightTypeElement te = new LightTypeElement(original.getManager(), ((PsiDisjunctionType)type).getLeastUpperBound());
return ChangeUtil.generateTreeElement(te, table, manager);
}
PsiClassType classType = (PsiClassType)type;
String text = classType.getPresentableText();
final TreeElement element = createType(original.getProject(), text, original, false);
PsiTypeElementImpl result = (PsiTypeElementImpl)SourceTreeToPsiMap.treeElementToPsi(element);
PsiTypeElementImpl result = SourceTreeToPsiMap.treeToPsiNotNull(element);
CodeEditUtil.setNodeGenerated(result, generated);
if (generated) {
@@ -397,7 +400,7 @@ public class JavaChangeUtilSupport implements TreeGenerator, TreeCopyHandler {
case PsiJavaCodeReferenceElementImpl.CLASS_NAME_KIND:
case PsiJavaCodeReferenceElementImpl.CLASS_OR_PACKAGE_NAME_KIND:
case PsiJavaCodeReferenceElementImpl.CLASS_IN_QUALIFIED_NEW_KIND:
final PsiElement target = ((PsiJavaCodeReferenceElement)SourceTreeToPsiMap.treeElementToPsi(original)).resolve();
final PsiElement target = SourceTreeToPsiMap.<PsiJavaCodeReferenceElement>treeToPsiNotNull(original).resolve();
if (target instanceof PsiClass) {
ref.putCopyableUserData(REFERENCED_CLASS_KEY, (PsiClass)target);
}
@@ -49,7 +49,6 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager {
private final PsiElementFactory myFactory;
private final SmartTypePointerManager mySmartTypePointerManager;
private ExpectedTypesProvider.ExpectedClassProvider myOccurrenceClassProvider;
private ExpectedTypesProvider myExpectedTypesProvider;
public TypeSelectorManagerImpl(Project project, PsiType type, PsiExpression mainOccurrence, PsiExpression[] occurrences) {
this(project, type, null, mainOccurrence, occurrences);
@@ -65,12 +64,12 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager {
setDefaultType(type);
myMainOccurrence = null;
myOccurrences = occurrences;
myExpectedTypesProvider = ExpectedTypesProvider.getInstance(project);
myOccurrenceClassProvider = createOccurrenceClassProvider();
myTypesForAll = getTypesForAll(areTypesDirected);
myTypesForMain = PsiType.EMPTY_ARRAY;
myIsOneSuggestion = myTypesForAll.length == 1;
myIsOneSuggestion = myTypesForAll.length == 1;
if (myIsOneSuggestion) {
myTypeSelector = new TypeSelector(myTypesForAll[0]);
}
@@ -90,7 +89,6 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager {
setDefaultType(type);
myMainOccurrence = mainOccurrence;
myOccurrences = occurrences;
myExpectedTypesProvider = ExpectedTypesProvider.getInstance(project);
myOccurrenceClassProvider = createOccurrenceClassProvider();
myTypesForMain = getTypesForMain();
@@ -149,8 +147,8 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager {
private ExpectedTypesProvider.ExpectedClassProvider createOccurrenceClassProvider() {
final Set<PsiClass> occurrenceClasses = new HashSet<PsiClass>();
for (final PsiExpression occurence : myOccurrences) {
final PsiType occurrenceType = occurence.getType();
for (final PsiExpression occurrence : myOccurrences) {
final PsiType occurrenceType = occurrence.getType();
final PsiClass aClass = PsiUtil.resolveClassInType(occurrenceType);
if (aClass != null) {
occurrenceClasses.add(aClass);
@@ -160,8 +158,7 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager {
}
private PsiType[] getTypesForMain() {
final ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(myMainOccurrence, false, myOccurrenceClassProvider,
false);
final ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(myMainOccurrence, false, myOccurrenceClassProvider, false);
final ArrayList<PsiType> allowedTypes = new ArrayList<PsiType>();
RefactoringHierarchyUtil.processSuperTypes(getDefaultType(), new RefactoringHierarchyUtil.SuperTypeVisitor() {
public void visitType(PsiType aType) {
@@ -173,9 +170,8 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager {
}
private void checkIfAllowed(PsiType type) {
if (expectedTypes != null && expectedTypes.length > 0) {
final ExpectedTypeInfo
typeInfo = ExpectedTypesProvider.createInfo(type, ExpectedTypeInfo.TYPE_STRICTLY, type, TailType.NONE);
if (expectedTypes.length > 0) {
final ExpectedTypeInfo typeInfo = ExpectedTypesProvider.createInfo(type, ExpectedTypeInfo.TYPE_STRICTLY, type, TailType.NONE);
for (ExpectedTypeInfo expectedType : expectedTypes) {
if (expectedType.intersect(typeInfo).length != 0) {
allowedTypes.add(type);
@@ -196,9 +192,7 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager {
private PsiType[] getTypesForAll(final boolean areTypesDirected) {
final ArrayList<ExpectedTypeInfo[]> expectedTypesFromAll = new ArrayList<ExpectedTypeInfo[]>();
for (PsiExpression occurrence : myOccurrences) {
final ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(occurrence, false, myOccurrenceClassProvider,
isUsedAfter());
final ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(occurrence, false, myOccurrenceClassProvider, isUsedAfter());
if (expectedTypes.length > 0) {
expectedTypesFromAll.add(expectedTypes);
}
@@ -266,13 +260,15 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager {
result.add(0, boxedType);
}
}
result.add(0, defaultType);
if (!TypeConversionUtil.isComposite(defaultType)) {
result.add(0, defaultType);
}
return result;
}
public void setAllOccurences(boolean allOccurences) {
public void setAllOccurences(boolean occurrences) {
if (myIsOneSuggestion) return;
setTypesAndPreselect(allOccurences ? myTypesForAll : myTypesForMain);
setTypesAndPreselect(occurrences ? myTypesForAll : myTypesForMain);
}
private void setTypesAndPreselect(PsiType[] types) {
@@ -0,0 +1,11 @@
class C {
static class E1 extends Exception { }
static class E2 extends Exception { }
void m() {
try { }
catch (E1 | E2 ex) {
final Exception e = ex;
}
}
}
@@ -0,0 +1,11 @@
class C {
static class E1 extends Exception { }
static class E2 extends Exception { }
void m() {
try { }
catch (E1 | E2 ex) {
<caret>ex;
}
}
}
@@ -0,0 +1,12 @@
class C {
interface B<T> { }
static class E1 extends Exception implements B<Integer> { }
static class E2 extends Exception implements B<Long> { }
void m() {
try { }
catch (E1 | E2 ex) {
final B<? extends Number> b = ex;
}
}
}
@@ -0,0 +1,12 @@
class C {
interface B<T> { }
static class E1 extends Exception implements B<Integer> { }
static class E2 extends Exception implements B<Long> { }
void m() {
try { }
catch (E1 | E2 ex) {
<caret>ex;
}
}
}
@@ -3,8 +3,6 @@ package com.intellij.refactoring;
import com.intellij.JavaTestUtil;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.JavaSdkImpl;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiType;
@@ -250,6 +248,14 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase {
});
}
public void testMultiCatchSimple() throws Exception {
doTest(new MockIntroduceVariableHandler("e", true, true, false, "C.E1 | C.E2"));
}
public void testMultiCatchTyped() throws Exception {
doTest(new MockIntroduceVariableHandler("b", true, true, false, "C.E1 | C.E2"));
}
private void doTest(IntroduceVariableBase testMe) throws Exception {
@NonNls String baseName = "/refactoring/introduceVariable/" + getTestName(false);
configureByFile(baseName + ".java");
@@ -18,39 +18,38 @@ package com.intellij.psi;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.*;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
/**
* Composite type resulting from Project Coin's multi-catch statements, * i.e. <code>FileNotFoundException | EOFException</code>.
* In most cases should be threatened via its least upper bound * (<code>IOException</code> in the example above).
*/
public class PsiDisjunctionType extends PsiType {
private final PsiTypeElement myTypeElement;
private final PsiManager myManager;
private final List<PsiType> myTypes;
private final CachedValue<PsiType> myLubCache;
public PsiDisjunctionType(final PsiTypeElement typeElement) {
public PsiDisjunctionType(final List<PsiType> types, final PsiManager psiManager) {
super(PsiAnnotation.EMPTY_ARRAY);
myTypeElement = typeElement;
myManager = psiManager;
myTypes = Collections.unmodifiableList(types);
final List<PsiTypeElement> typeElements = PsiTreeUtil.getChildrenOfTypeAsList(myTypeElement, PsiTypeElement.class);
myTypes = Collections.unmodifiableList(ContainerUtil.map(typeElements, new Function<PsiTypeElement, PsiType>() {
@Override
public PsiType fun(final PsiTypeElement psiTypeElement) {
return psiTypeElement.getType();
}
}));
final CachedValuesManager cacheManager = CachedValuesManager.getManager(myTypeElement.getProject());
final CachedValuesManager cacheManager = CachedValuesManager.getManager(psiManager.getProject());
myLubCache = cacheManager.createCachedValue(new CachedValueProvider<PsiType>() {
public Result<PsiType> compute() {
PsiType lub = myTypes.get(0);
for (int i = 1; i < myTypes.size(); i++) {
lub = GenericsUtil.getLeastUpperBound(lub, myTypes.get(i), myTypeElement.getManager());
lub = GenericsUtil.getLeastUpperBound(lub, myTypes.get(i), psiManager);
}
return Result.create(lub, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
}
@@ -65,6 +64,10 @@ public class PsiDisjunctionType extends PsiType {
return myTypes;
}
public PsiManager getManager() {
return myManager;
}
@Override
public String getPresentableText() {
return StringUtil.join(myTypes, new Function<PsiType, String>() {
@@ -101,11 +104,7 @@ public class PsiDisjunctionType extends PsiType {
@Override
public <A> A accept(final PsiTypeVisitor<A> visitor) {
final PsiType lub = getLeastUpperBound();
if (lub instanceof PsiClassType) {
return visitor.visitClassType((PsiClassType)lub);
}
return visitor.visitType(lub);
return visitor.visitDisjunctionType(this);
}
@Override
@@ -48,4 +48,8 @@ public class PsiTypeVisitor<A> {
public A visitEllipsisType(PsiEllipsisType ellipsisType) {
return visitArrayType(ellipsisType);
}
public A visitDisjunctionType(PsiDisjunctionType disjunctionType) {
return visitType(disjunctionType);
}
}
@@ -33,10 +33,7 @@ import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.*;
public class TypeConversionUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.TypeConversionUtil");
@@ -75,6 +72,7 @@ public class TypeConversionUtil {
TYPE_TO_RANK_MAP.put(PsiType.BOOLEAN, BOOL_RANK);
}
private TypeConversionUtil() { }
/**
* @return true if fromType can be casted to toType
@@ -1077,6 +1075,10 @@ public class TypeConversionUtil {
return type != null && isPrimitiveWrapper(type.getCanonicalText());
}
public static boolean isComposite(final PsiType type) {
return type instanceof PsiDisjunctionType || type instanceof PsiIntersectionType;
}
public static PsiType typeParameterErasure(@NotNull PsiTypeParameter typeParameter) {
return typeParameterErasure(typeParameter, PsiSubstitutor.EMPTY);
}
@@ -1122,9 +1124,10 @@ public class TypeConversionUtil {
return erasure(type, PsiSubstitutor.EMPTY);
}
public static PsiType erasure(PsiType type, final PsiSubstitutor beforeSubstitutor) {
public static PsiType erasure(final PsiType type, final PsiSubstitutor beforeSubstitutor) {
if (type == null) return null;
return type.accept(new PsiTypeVisitor<PsiType>() {
@Override
public PsiType visitClassType(PsiClassType classType) {
final PsiClass aClass = classType.resolve();
if (aClass instanceof PsiTypeParameter) {
@@ -1135,14 +1138,17 @@ public class TypeConversionUtil {
}
}
@Override
public PsiType visitWildcardType(PsiWildcardType wildcardType) {
return wildcardType.getExtendsBound().accept(this);
}
@Override
public PsiType visitPrimitiveType(PsiPrimitiveType primitiveType) {
return primitiveType;
}
@Override
public PsiType visitEllipsisType(PsiEllipsisType ellipsisType) {
final PsiType componentType = ellipsisType.getComponentType();
final PsiType newComponentType = componentType.accept(this);
@@ -1150,12 +1156,23 @@ public class TypeConversionUtil {
return new PsiArrayType(newComponentType);
}
@Override
public PsiType visitArrayType(PsiArrayType arrayType) {
final PsiType componentType = arrayType.getComponentType();
final PsiType newComponentType = componentType.accept(this);
if (newComponentType == componentType) return arrayType;
return newComponentType.createArrayType();
}
@Override
public PsiType visitDisjunctionType(PsiDisjunctionType disjunctionType) {
final List<PsiType> original = disjunctionType.getDisjunctions();
final List<PsiType> erased = new ArrayList<PsiType>(original.size());
for (PsiType psiType : original) {
erased.add(erasure(psiType, beforeSubstitutor));
}
return new PsiDisjunctionType(erased, disjunctionType.getManager());
}
});
}
@@ -1539,6 +1556,5 @@ public class TypeConversionUtil {
}
return new ClassCandidateInfo(clazz, result.getSubstitutor());
}
}