PsiTypesUtil: reverted erroneous changes in getExpectedTypeByParent (IDEA-CR-15590)
[idea/community.git] / java / java-psi-api / src / com / intellij / psi / util / PsiTypesUtil.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.psi.util;
17
18 import com.intellij.lang.ASTNode;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.util.Comparing;
21 import com.intellij.openapi.util.Condition;
22 import com.intellij.pom.java.LanguageLevel;
23 import com.intellij.psi.*;
24 import com.intellij.psi.tree.IElementType;
25 import com.intellij.util.ArrayUtil;
26 import gnu.trove.THashMap;
27 import org.jetbrains.annotations.NonNls;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
30
31 import java.util.HashSet;
32 import java.util.Map;
33 import java.util.Set;
34
35 public class PsiTypesUtil {
36   @NonNls private static final Map<String, String> ourUnboxedTypes = new THashMap<String, String>();
37   @NonNls private static final Map<String, String> ourBoxedTypes = new THashMap<String, String>();
38
39   static {
40     ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_BOOLEAN, "boolean");
41     ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_BYTE, "byte");
42     ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_SHORT, "short");
43     ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_INTEGER, "int");
44     ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_LONG, "long");
45     ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_FLOAT, "float");
46     ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_DOUBLE, "double");
47     ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_CHARACTER, "char");
48
49     ourBoxedTypes.put("boolean", CommonClassNames.JAVA_LANG_BOOLEAN);
50     ourBoxedTypes.put("byte", CommonClassNames.JAVA_LANG_BYTE);
51     ourBoxedTypes.put("short", CommonClassNames.JAVA_LANG_SHORT);
52     ourBoxedTypes.put("int", CommonClassNames.JAVA_LANG_INTEGER);
53     ourBoxedTypes.put("long", CommonClassNames.JAVA_LANG_LONG);
54     ourBoxedTypes.put("float", CommonClassNames.JAVA_LANG_FLOAT);
55     ourBoxedTypes.put("double", CommonClassNames.JAVA_LANG_DOUBLE);
56     ourBoxedTypes.put("char", CommonClassNames.JAVA_LANG_CHARACTER);
57   }
58
59   @NonNls private static final String GET_CLASS_METHOD = "getClass";
60
61   private PsiTypesUtil() { }
62
63   @NotNull
64   public static String getDefaultValueOfType(PsiType type) {
65     if (type instanceof PsiArrayType) {
66       int count = type.getArrayDimensions() - 1;
67       PsiType componentType = type.getDeepComponentType();
68
69       if (componentType instanceof PsiClassType) {
70         final PsiClassType classType = (PsiClassType)componentType;
71         if (classType.resolve() instanceof PsiTypeParameter) {
72           return PsiKeyword.NULL;
73         }
74       }
75
76       StringBuilder buffer = new StringBuilder();
77       buffer.append(PsiKeyword.NEW);
78       buffer.append(" ");
79       buffer.append(componentType.getCanonicalText());
80       buffer.append("[0]");
81       for (int i = 0; i < count; i++) {
82         buffer.append("[]");
83       }
84       return buffer.toString();
85     }
86     if (type instanceof PsiPrimitiveType) {
87       return PsiType.BOOLEAN.equals(type) ? PsiKeyword.FALSE : "0";
88     }
89     return PsiKeyword.NULL;
90   }
91
92   /**
93    * Returns the unboxed type name or parameter.
94    * @param type boxed java type name
95    * @return unboxed type name if available; same value otherwise
96    */
97   @Nullable
98   public static String unboxIfPossible(final String type) {
99     if (type == null) return null;
100     final String s = ourUnboxedTypes.get(type);
101     return s == null? type : s;
102   }
103
104   /**
105    * Returns the boxed type name or parameter.
106    * @param type primitive java type name
107    * @return boxed type name if available; same value otherwise
108    */
109   @Nullable
110   public static String boxIfPossible(final String type) {
111     if (type == null) return null;
112     final String s = ourBoxedTypes.get(type);
113     return s == null ? type : s;
114   }
115
116   @Nullable
117   public static PsiClass getPsiClass(@Nullable PsiType psiType) {
118     return psiType instanceof PsiClassType? ((PsiClassType)psiType).resolve() : null;
119   }
120
121   public static PsiClassType getClassType(@NotNull PsiClass psiClass) {
122     return JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass);
123   }
124
125   @Nullable
126   public static PsiClassType getLowestUpperBoundClassType(@NotNull final PsiDisjunctionType type) {
127     final PsiType lub = type.getLeastUpperBound();
128     if (lub instanceof PsiClassType) {
129       return (PsiClassType)lub;
130     }
131     else if (lub instanceof PsiIntersectionType) {
132       for (PsiType subType : ((PsiIntersectionType)lub).getConjuncts()) {
133         if (subType instanceof PsiClassType) {
134           final PsiClass aClass = ((PsiClassType)subType).resolve();
135           if (aClass != null && !aClass.isInterface()) {
136             return (PsiClassType)subType;
137           }
138         }
139       }
140     }
141     return null;
142   }
143
144   public static PsiType patchMethodGetClassReturnType(@NotNull PsiMethodReferenceExpression methodExpression,
145                                                       @NotNull PsiMethod method) {
146     if (isGetClass(method)) {
147       final PsiType qualifierType = PsiMethodReferenceUtil.getQualifierType(methodExpression);
148       return qualifierType != null ? createJavaLangClassType(methodExpression, qualifierType, true) : null;
149     }
150     return null;
151   }
152   
153   public static PsiType patchMethodGetClassReturnType(@NotNull PsiExpression call,
154                                                       @NotNull PsiReferenceExpression methodExpression,
155                                                       @NotNull PsiMethod method,
156                                                       @Nullable Condition<IElementType> condition,
157                                                       @NotNull LanguageLevel languageLevel) {
158     //JLS3 15.8.2
159     if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5) && isGetClass(method)) {
160       PsiExpression qualifier = methodExpression.getQualifierExpression();
161       PsiType qualifierType = null;
162       final Project project = call.getProject();
163       if (qualifier != null) {
164         qualifierType = TypeConversionUtil.erasure(qualifier.getType());
165       }
166       else if (condition != null) {
167         ASTNode parent = call.getNode().getTreeParent();
168         while (parent != null && condition.value(parent.getElementType())) {
169           parent = parent.getTreeParent();
170         }
171         if (parent != null) {
172           qualifierType = JavaPsiFacade.getInstance(project).getElementFactory().createType((PsiClass)parent.getPsi());
173         }
174       }
175       return createJavaLangClassType(methodExpression, qualifierType, true);
176     }
177     return null;
178   }
179
180   public static boolean isGetClass(PsiMethod method) {
181     return GET_CLASS_METHOD.equals(method.getName()) && CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName());
182   }
183
184   @Nullable
185   public static PsiType createJavaLangClassType(@NotNull PsiElement context, @Nullable PsiType qualifierType, boolean captureTopLevelWildcards) {
186     if (qualifierType != null) {
187       PsiUtil.ensureValidType(qualifierType);
188       JavaPsiFacade facade = JavaPsiFacade.getInstance(context.getProject());
189       PsiClass javaLangClass = facade.findClass(CommonClassNames.JAVA_LANG_CLASS, context.getResolveScope());
190       if (javaLangClass != null && javaLangClass.getTypeParameters().length == 1) {
191         PsiSubstitutor substitutor = PsiSubstitutor.EMPTY.
192           put(javaLangClass.getTypeParameters()[0], PsiWildcardType.createExtends(context.getManager(), qualifierType));
193         final PsiClassType classType = facade.getElementFactory().createType(javaLangClass, substitutor, PsiUtil.getLanguageLevel(context));
194         return captureTopLevelWildcards ? PsiUtil.captureToplevelWildcards(classType, context) : classType;
195       }
196     }
197     return null;
198   }
199
200   @Nullable
201   public static PsiType getExpectedTypeByParent(PsiElement element) {
202     final PsiElement parent = PsiUtil.skipParenthesizedExprUp(element.getParent());
203     if (parent instanceof PsiVariable) {
204       if (PsiUtil.checkSameExpression(element, ((PsiVariable)parent).getInitializer())) {
205         return ((PsiVariable)parent).getType();
206       }
207     }
208     else if (parent instanceof PsiAssignmentExpression) {
209       if (PsiUtil.checkSameExpression(element, ((PsiAssignmentExpression)parent).getRExpression())) {
210         return ((PsiAssignmentExpression)parent).getLExpression().getType();
211       }
212     }
213     else if (parent instanceof PsiReturnStatement) {
214       final PsiElement psiElement = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class, PsiMethod.class);
215       if (psiElement instanceof PsiLambdaExpression) {
216         return null;
217       }
218       else if (psiElement instanceof PsiMethod){
219         return ((PsiMethod)psiElement).getReturnType();
220       }
221     }
222     else if (PsiUtil.isCondition(element, parent)) {
223       return PsiType.BOOLEAN.getBoxedType(parent);
224     } 
225     else if (parent instanceof PsiArrayInitializerExpression) {
226       final PsiElement gParent = parent.getParent();
227       if (gParent instanceof PsiNewExpression) {
228         final PsiType type = ((PsiNewExpression)gParent).getType();
229         if (type instanceof PsiArrayType) {
230           return ((PsiArrayType)type).getComponentType();
231         }
232       }
233       else if (gParent instanceof PsiVariable) {
234         final PsiType type = ((PsiVariable)gParent).getType();
235         if (type instanceof PsiArrayType) {
236           return ((PsiArrayType)type).getComponentType();
237         }
238       }
239       else if (gParent instanceof PsiArrayInitializerExpression) {
240         final PsiType expectedTypeByParent = getExpectedTypeByParent(parent);
241         return expectedTypeByParent != null && expectedTypeByParent instanceof PsiArrayType
242                ? ((PsiArrayType)expectedTypeByParent).getComponentType() : null;
243       }
244     }
245     return null;
246   }
247
248   /**
249    * Returns the return type for enclosing method or lambda
250    *
251    * @param element element inside method or lambda to determine the return type of
252    * @return the return type or null if cannot be determined
253    */
254   @Nullable
255   public static PsiType getMethodReturnType(PsiElement element) {
256     final PsiElement methodOrLambda = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiLambdaExpression.class);
257     return methodOrLambda instanceof PsiMethod
258            ? ((PsiMethod)methodOrLambda).getReturnType()
259            : methodOrLambda instanceof PsiLambdaExpression ? LambdaUtil.getFunctionalInterfaceReturnType((PsiLambdaExpression)methodOrLambda) : null;
260   }
261
262   public static boolean compareTypes(PsiType leftType, PsiType rightType, boolean ignoreEllipsis) {
263     if (ignoreEllipsis) {
264       if (leftType instanceof PsiEllipsisType) {
265         leftType = ((PsiEllipsisType)leftType).toArrayType();
266       }
267       if (rightType instanceof PsiEllipsisType) {
268         rightType = ((PsiEllipsisType)rightType).toArrayType();
269       }
270     }
271     return Comparing.equal(leftType, rightType);
272   }
273
274   public static boolean isDenotableType(PsiType type) {
275     if (type instanceof PsiWildcardType || type instanceof PsiCapturedWildcardType) {
276       return false;
277     }
278     return true;
279   }
280   
281   public static boolean hasUnresolvedComponents(@NotNull PsiType type) {
282     return type.accept(new PsiTypeVisitor<Boolean>() {
283       @Nullable
284       @Override
285       public Boolean visitClassType(PsiClassType classType) {
286         final PsiClass psiClass = classType.resolve();
287         if (psiClass == null) {
288           return true;
289         }
290         for (PsiType param : classType.getParameters()) {
291           if (param.accept(this)) {
292             return true;
293           }
294         }
295         return super.visitClassType(classType);
296       }
297
298       @Nullable
299       @Override
300       public Boolean visitArrayType(PsiArrayType arrayType) {
301         return arrayType.getComponentType().accept(this);
302       }
303
304       @Nullable
305       @Override
306       public Boolean visitWildcardType(PsiWildcardType wildcardType) {
307         final PsiType bound = wildcardType.getBound();
308         return bound != null && bound.accept(this);
309       }
310
311       @Override
312       public Boolean visitType(PsiType type) {
313         return false;
314       }
315     });
316   }
317
318   public static PsiType getParameterType(PsiParameter[] parameters, int i, boolean varargs) {
319     final PsiParameter parameter = parameters[i < parameters.length ? i : parameters.length - 1];
320     PsiType parameterType = parameter.getType();
321     if (parameterType instanceof PsiEllipsisType && varargs) {
322       parameterType = ((PsiEllipsisType)parameterType).getComponentType();
323     }
324     if (!parameterType.isValid()) {
325       PsiUtil.ensureValidType(parameterType, "Invalid type of parameter " + parameter + " of " + parameter.getClass());
326     }
327     return parameterType;
328   }
329
330   public static PsiTypeParameter[] filterUnusedTypeParameters(final PsiType superReturnTypeInBaseClassType,
331                                                               final PsiTypeParameter[] typeParameters) {
332     if (typeParameters.length == 0) return typeParameters;
333
334     final Set<PsiTypeParameter> usedParameters = new HashSet<PsiTypeParameter>();
335     superReturnTypeInBaseClassType.accept(new PsiTypeVisitor<Object>(){
336       @Nullable
337       @Override
338       public Object visitClassType(PsiClassType classType) {
339         final PsiClass aClass = classType.resolve();
340         if (aClass instanceof PsiTypeParameter && ArrayUtil.find(typeParameters, aClass) > -1) {
341           usedParameters.add((PsiTypeParameter)aClass);
342           return null;
343         }
344         for (PsiType type : classType.getParameters()) {
345           type.accept(this);
346         }
347         return null;
348       }
349
350       @Nullable
351       @Override
352       public Object visitWildcardType(PsiWildcardType wildcardType) {
353         final PsiType bound = wildcardType.getBound();
354         return bound != null ? bound.accept(this) : null;
355       }
356
357       @Nullable
358       @Override
359       public Object visitArrayType(PsiArrayType arrayType) {
360         return arrayType.getComponentType().accept(this);
361       }
362     });
363     return usedParameters.toArray(new PsiTypeParameter[usedParameters.size()]);
364   }
365 }