java: prohibit caching when using thread-local types imposed on expressions and decla...
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / source / tree / java / PsiMethodCallExpressionImpl.java
1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.psi.impl.source.tree.java;
3
4 import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
5 import com.intellij.lang.ASTNode;
6 import com.intellij.openapi.diagnostic.Logger;
7 import com.intellij.openapi.projectRoots.JavaSdkVersion;
8 import com.intellij.openapi.projectRoots.JavaVersionService;
9 import com.intellij.openapi.util.Comparing;
10 import com.intellij.pom.java.LanguageLevel;
11 import com.intellij.psi.*;
12 import com.intellij.psi.impl.DebugUtil;
13 import com.intellij.psi.impl.PsiClassImplUtil;
14 import com.intellij.psi.impl.source.resolve.JavaResolveCache;
15 import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
16 import com.intellij.psi.impl.source.tree.ChildRole;
17 import com.intellij.psi.impl.source.tree.ElementType;
18 import com.intellij.psi.impl.source.tree.JavaElementType;
19 import com.intellij.psi.infos.MethodCandidateInfo;
20 import com.intellij.psi.tree.ChildRoleBase;
21 import com.intellij.psi.tree.IElementType;
22 import com.intellij.psi.util.PsiTreeUtil;
23 import com.intellij.psi.util.PsiTypesUtil;
24 import com.intellij.psi.util.PsiUtil;
25 import com.intellij.psi.util.TypeConversionUtil;
26 import com.intellij.util.Function;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
29
30 import java.util.Arrays;
31
32 public class PsiMethodCallExpressionImpl extends ExpressionPsiElement implements PsiMethodCallExpression {
33   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiMethodCallExpressionImpl");
34
35   public PsiMethodCallExpressionImpl() {
36     super(JavaElementType.METHOD_CALL_EXPRESSION);
37   }
38
39   @Override
40   public PsiType getType() {
41     return JavaResolveCache.getInstance(getProject()).getType(this, ourTypeEvaluator);
42   }
43
44   @Override
45   public PsiMethod resolveMethod() {
46     return (PsiMethod)getMethodExpression().resolve();
47   }
48
49   @Override
50   @NotNull
51   public JavaResolveResult resolveMethodGenerics() {
52     return getMethodExpression().advancedResolve(false);
53   }
54
55   @Override
56   public void removeChild(@NotNull ASTNode child) {
57     if (child == getArgumentList()) {
58       LOG.error("Cannot delete argument list since it will break contract on argument list notnullity");
59     }
60     super.removeChild(child);
61   }
62
63   @Override
64   @NotNull
65   public PsiReferenceParameterList getTypeArgumentList() {
66     PsiReferenceExpression expression = getMethodExpression();
67     PsiReferenceParameterList result = expression.getParameterList();
68     if (result != null) return result;
69     LOG.error("Invalid method call expression. Children:\n" + DebugUtil.psiTreeToString(expression, false));
70     return result;
71   }
72
73   @Override
74   @NotNull
75   public PsiType[] getTypeArguments() {
76     return getMethodExpression().getTypeParameters();
77   }
78
79   @Override
80   @NotNull
81   public PsiReferenceExpression getMethodExpression() {
82     return (PsiReferenceExpression)findChildByRoleAsPsiElement(ChildRole.METHOD_EXPRESSION);
83   }
84
85   @Override
86   @NotNull
87   public PsiExpressionList getArgumentList() {
88     PsiExpressionList list = (PsiExpressionList)findChildByRoleAsPsiElement(ChildRole.ARGUMENT_LIST);
89     if (list == null) {
90       LOG.error("Invalid PSI for'" + getText() + ". Parent:" + DebugUtil.psiToString(getParent(), false));
91     }
92     return list;
93   }
94
95   @Override
96   public ASTNode findChildByRole(int role) {
97     LOG.assertTrue(ChildRole.isUnique(role));
98     switch (role) {
99       default:
100         return null;
101
102       case ChildRole.METHOD_EXPRESSION:
103         return getFirstChildNode();
104
105       case ChildRole.ARGUMENT_LIST:
106         return findChildByType(JavaElementType.EXPRESSION_LIST);
107     }
108   }
109
110   @Override
111   public int getChildRole(@NotNull ASTNode child) {
112     LOG.assertTrue(child.getTreeParent() == this);
113     IElementType i = child.getElementType();
114     if (i == JavaElementType.EXPRESSION_LIST) {
115       return ChildRole.ARGUMENT_LIST;
116     }
117     else {
118       if (ElementType.EXPRESSION_BIT_SET.contains(child.getElementType())) {
119         return ChildRole.METHOD_EXPRESSION;
120       }
121       return ChildRoleBase.NONE;
122     }
123   }
124
125   @Override
126   public void accept(@NotNull PsiElementVisitor visitor) {
127     if (visitor instanceof JavaElementVisitor) {
128       ((JavaElementVisitor)visitor).visitMethodCallExpression(this);
129     }
130     else {
131       visitor.visitElement(this);
132     }
133   }
134
135   @Override
136   public String toString() {
137     return "PsiMethodCallExpression:" + getText();
138   }
139
140   private static final TypeEvaluator ourTypeEvaluator = new TypeEvaluator();
141
142   private static class TypeEvaluator implements Function<PsiMethodCallExpression, PsiType> {
143     @Override
144     @Nullable
145     public PsiType fun(final PsiMethodCallExpression call) {
146       PsiReferenceExpression methodExpression = call.getMethodExpression();
147       final JavaResolveResult[] results = methodExpression.multiResolve(false);
148       PsiFile file = call.getContainingFile();
149       LanguageLevel languageLevel = PsiUtil.getLanguageLevel(file);
150
151       final PsiElement callParent = PsiUtil.skipParenthesizedExprUp(call.getParent());
152       final PsiExpressionList parentArgList;
153       if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
154         parentArgList = callParent instanceof PsiConditionalExpression && !PsiPolyExpressionUtil.isPolyExpression((PsiExpression)callParent)
155                         ? null : PsiTreeUtil.getParentOfType(call, PsiExpressionList.class, true, PsiReferenceExpression.class);
156       }
157       else {
158         parentArgList = null;
159       }
160       final boolean genericParentOverloadResolution = parentArgList != null && 
161                                                       MethodCandidateInfo.isOverloadCheck(parentArgList) &&
162                                                       Arrays.stream(parentArgList.getExpressions())
163                                                         .map(expression -> PsiUtil.skipParenthesizedExprDown(expression))
164                                                         .noneMatch(expression -> expression != null && ThreadLocalTypes.hasBindingFor(expression));
165
166       PsiType theOnly = null;
167       for (int i = 0; i < results.length; i++) {
168         final JavaResolveResult candidateInfo = results[i];
169
170         if (genericParentOverloadResolution && PsiPolyExpressionUtil.isMethodCallPolyExpression(call, (PsiMethod)candidateInfo.getElement())) {
171           LOG.error("poly expression evaluation during overload resolution");
172         }
173
174         final PsiType type = getResultType(call, methodExpression, candidateInfo, languageLevel);
175         if (type == null) {
176           return null;
177         }
178
179         if (i == 0) {
180           theOnly = type;
181         }
182         else if (!theOnly.equals(type)) {
183           return null;
184         }
185       }
186
187       return PsiClassImplUtil.correctType(theOnly, file.getResolveScope());
188     }
189
190     @Nullable
191     private static PsiType getResultType(@NotNull PsiMethodCallExpression call,
192                                          @NotNull PsiReferenceExpression methodExpression,
193                                          @NotNull JavaResolveResult result,
194                                          @NotNull final LanguageLevel languageLevel) {
195       final PsiMethod method = (PsiMethod)result.getElement();
196       if (method == null) return null;
197
198       boolean is15OrHigher = languageLevel.compareTo(LanguageLevel.JDK_1_5) >= 0;
199       final PsiType getClassReturnType = PsiTypesUtil.patchMethodGetClassReturnType(call, methodExpression, method,
200                                                                                     type -> type != JavaElementType.CLASS && 
201                                                                                              //enum can be created inside enum only, no need to mention it here
202                                                                                             type != JavaElementType.ANONYMOUS_CLASS
203                                                                                     , languageLevel);
204
205       if (getClassReturnType != null) {
206         return getClassReturnType;
207       }
208
209       PsiType ret = method.getReturnType();
210       if (ret == null) return null;
211       if (ret instanceof PsiClassType) {
212         ret = ((PsiClassType)ret).setLanguageLevel(languageLevel);
213       }
214       if (is15OrHigher) {
215         return captureReturnType(call, method, ret, result, languageLevel);
216       }
217       return TypeConversionUtil.erasure(ret);
218     }
219   }
220
221   private static PsiType captureReturnType(PsiMethodCallExpression call,
222                                            PsiMethod method,
223                                            PsiType ret,
224                                            JavaResolveResult result,
225                                            LanguageLevel languageLevel) {
226     PsiSubstitutor substitutor = result.getSubstitutor();
227     PsiType substitutedReturnType = substitutor.substitute(ret);
228     if (substitutedReturnType == null) {
229       return TypeConversionUtil.erasure(ret);
230     }
231
232     if (result instanceof MethodCandidateInfo && ((MethodCandidateInfo)result).isErased()) {
233       // 18.5.2
234       // if unchecked conversion was necessary, then this substitution provides the parameter types of the invocation type, 
235       // while the return type and thrown types are given by the erasure of m's type (without applying theta').
236       //due to https://bugs.openjdk.java.net/browse/JDK-8135087 erasure is called on substitutedReturnType and not on ret type itself as by spec
237       return TypeConversionUtil.erasure(substitutedReturnType);
238     }
239
240     //15.12.2.6. Method Invocation Type
241     // If unchecked conversion was necessary for the method to be applicable, 
242     // the parameter types of the invocation type are the parameter types of the method's type,
243     // and the return type and thrown types are given by the erasures of the return type and thrown types of the method's type.
244     if (((!languageLevel.isAtLeast(LanguageLevel.JDK_1_8) || call.getTypeArguments().length > 0) && method.hasTypeParameters() ||
245          !method.hasTypeParameters() && JavaVersionService.getInstance().isAtLeast(call, JavaSdkVersion.JDK_1_8)) &&
246         result instanceof MethodCandidateInfo && ((MethodCandidateInfo)result).isApplicable()) {
247       final PsiType[] args = call.getArgumentList().getExpressionTypes();
248       final PsiParameter[] parameters = method.getParameterList().getParameters();
249       final boolean varargs = ((MethodCandidateInfo)result).getApplicabilityLevel() == MethodCandidateInfo.ApplicabilityLevel.VARARGS;
250       for (int i = 0; i < args.length; i++) {
251         final PsiType parameterType = substitutor.substitute(PsiTypesUtil.getParameterType(parameters, i, varargs));
252         final PsiType expressionType = args[i];
253         if (expressionType != null && parameterType != null && JavaGenericsUtil.isRawToGeneric(parameterType, expressionType)) {
254           return TypeConversionUtil.erasure(substitutedReturnType);
255         }
256       }
257     }
258
259     if (PsiUtil.isRawSubstitutor(method, substitutor)) {
260       final PsiType returnTypeErasure = TypeConversionUtil.erasure(ret);
261       if (Comparing.equal(TypeConversionUtil.erasure(substitutedReturnType), returnTypeErasure)) {
262         return returnTypeErasure;
263       }
264     }
265     return PsiUtil.captureToplevelWildcards(substitutedReturnType, call);
266   }
267 }
268