java: prohibit caching when using thread-local types imposed on expressions and decla...
[idea/community.git] / java / java-psi-api / src / com / intellij / psi / LambdaUtil.java
1 // Copyright 2000-2019 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;
3
4 import com.intellij.openapi.diagnostic.Logger;
5 import com.intellij.openapi.util.registry.Registry;
6 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
7 import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
8 import com.intellij.psi.infos.MethodCandidateInfo;
9 import com.intellij.psi.util.*;
10 import com.intellij.util.Consumer;
11 import com.intellij.util.IncorrectOperationException;
12 import com.intellij.util.ObjectUtils;
13 import com.intellij.util.containers.ContainerUtil;
14 import org.jetbrains.annotations.Contract;
15 import org.jetbrains.annotations.NotNull;
16 import org.jetbrains.annotations.Nullable;
17
18 import java.util.*;
19 import java.util.function.Function;
20 import java.util.function.Supplier;
21
22 /**
23  * @author anna
24  */
25 public class LambdaUtil {
26   private static final Logger LOG = Logger.getInstance(LambdaUtil.class);
27
28   @Nullable
29   public static PsiType getFunctionalInterfaceReturnType(PsiFunctionalExpression expr) {
30     return getFunctionalInterfaceReturnType(expr.getFunctionalInterfaceType());
31   }
32
33   @Nullable
34   public static PsiType getFunctionalInterfaceReturnType(@Nullable PsiType functionalInterfaceType) {
35     final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
36     final PsiClass psiClass = resolveResult.getElement();
37     if (psiClass != null) {
38       final MethodSignature methodSignature = getFunction(psiClass);
39       if (methodSignature != null) {
40         final PsiType returnType = getReturnType(psiClass, methodSignature);
41         return resolveResult.getSubstitutor().substitute(returnType);
42       }
43     }
44     return null;
45   }
46
47   @Contract("null -> null")
48   @Nullable
49   public static PsiMethod getFunctionalInterfaceMethod(@Nullable PsiType functionalInterfaceType) {
50     return getFunctionalInterfaceMethod(PsiUtil.resolveGenericsClassInType(functionalInterfaceType));
51   }
52
53   public static PsiMethod getFunctionalInterfaceMethod(@Nullable PsiElement element) {
54     if (element instanceof PsiFunctionalExpression) {
55       final PsiType samType = ((PsiFunctionalExpression)element).getFunctionalInterfaceType();
56       return getFunctionalInterfaceMethod(samType);
57     }
58     return null;
59   }
60
61   @Nullable
62   public static PsiMethod getFunctionalInterfaceMethod(@NotNull PsiClassType.ClassResolveResult result) {
63     return getFunctionalInterfaceMethod(result.getElement());
64   }
65
66   @Contract("null -> null")
67   @Nullable
68   public static PsiMethod getFunctionalInterfaceMethod(PsiClass aClass) {
69     final MethodSignature methodSignature = getFunction(aClass);
70     if (methodSignature != null) {
71       return getMethod(aClass, methodSignature);
72     }
73     return null;
74   }
75
76   public static PsiSubstitutor getSubstitutor(@NotNull PsiMethod method, @NotNull PsiClassType.ClassResolveResult resolveResult) {
77     final PsiClass derivedClass = resolveResult.getElement();
78     LOG.assertTrue(derivedClass != null);
79
80     final PsiClass methodContainingClass = method.getContainingClass();
81     LOG.assertTrue(methodContainingClass != null);
82     PsiSubstitutor initialSubst = resolveResult.getSubstitutor();
83     final PsiSubstitutor superClassSubstitutor =
84       TypeConversionUtil.getSuperClassSubstitutor(methodContainingClass, derivedClass, PsiSubstitutor.EMPTY);
85     for (PsiTypeParameter param : superClassSubstitutor.getSubstitutionMap().keySet()) {
86       initialSubst = initialSubst.put(param, initialSubst.substitute(superClassSubstitutor.substitute(param)));
87     }
88     return initialSubst;
89   }
90
91   public static boolean isFunctionalType(PsiType type) {
92     if (type instanceof PsiIntersectionType) {
93       return extractFunctionalConjunct((PsiIntersectionType)type) != null;
94     }
95     return isFunctionalClass(PsiUtil.resolveClassInClassTypeOnly(type));
96   }
97
98   @Contract("null -> false")
99   public static boolean isFunctionalClass(PsiClass aClass) {
100     if (aClass != null) {
101       if (aClass instanceof PsiTypeParameter) return false;
102       return getFunction(aClass) != null;
103     }
104     return false;
105   }
106
107   @Contract("null -> false")
108   public static boolean isValidLambdaContext(@Nullable PsiElement context) {
109     context = PsiUtil.skipParenthesizedExprUp(context);
110     if (isAssignmentOrInvocationContext(context) || context instanceof PsiTypeCastExpression) {
111       return true;
112     }
113     if (context instanceof PsiConditionalExpression) {
114       PsiElement parentContext = PsiUtil.skipParenthesizedExprUp(context.getParent());
115       if (isAssignmentOrInvocationContext(parentContext)) return true;
116       if (parentContext instanceof PsiConditionalExpression) {
117         return isValidLambdaContext(parentContext);
118       }
119     }
120     if (context instanceof PsiBreakStatement) {
121       PsiElement element = ((PsiBreakStatement)context).findExitedElement();
122       if (element instanceof PsiSwitchExpression) {
123         return isValidLambdaContext(element.getParent());
124       }
125     }
126     if (context instanceof PsiExpressionStatement) {
127       PsiElement parent = context.getParent();
128       if (parent instanceof PsiSwitchLabeledRuleStatement) {
129         PsiSwitchBlock switchBlock = ((PsiSwitchLabeledRuleStatement)parent).getEnclosingSwitchBlock();
130         if (switchBlock != null) {
131           return isValidLambdaContext(switchBlock.getParent());
132         }
133       }
134     }
135     return false;
136   }
137
138   @Contract("null -> false")
139   private static boolean isAssignmentOrInvocationContext(PsiElement context) {
140     return isAssignmentContext(context) || isInvocationContext(context);
141   }
142
143   private static boolean isInvocationContext(@Nullable PsiElement context) {
144     return context instanceof PsiExpressionList;
145   }
146
147   private static boolean isAssignmentContext(PsiElement context) {
148     return context instanceof PsiLambdaExpression ||
149            context instanceof PsiReturnStatement ||
150            context instanceof PsiAssignmentExpression ||
151            context instanceof PsiVariable && !withInferredType((PsiVariable)context) ||
152            context instanceof PsiArrayInitializerExpression;
153   }
154
155   private static boolean withInferredType(PsiVariable variable) {
156     PsiTypeElement typeElement = variable.getTypeElement();
157     return typeElement != null && typeElement.isInferredType();
158   }
159
160   @Contract("null -> null")
161   @Nullable
162   public static MethodSignature getFunction(final PsiClass psiClass) {
163     if (isPlainInterface(psiClass)) {
164       return CachedValuesManager.getCachedValue(psiClass, () -> CachedValueProvider.Result
165         .create(calcFunction(psiClass), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT));
166     }
167     return null;
168   }
169
170   private static boolean isPlainInterface(PsiClass psiClass) {
171     return psiClass != null && psiClass.isInterface() && !psiClass.isAnnotationType();
172   }
173
174   @Nullable
175   private static MethodSignature calcFunction(@NotNull PsiClass psiClass) {
176     if (hasManyOwnAbstractMethods(psiClass) || hasManyInheritedAbstractMethods(psiClass)) return null;
177
178     final List<HierarchicalMethodSignature> functions = findFunctionCandidates(psiClass);
179     return functions != null && functions.size() == 1 ? functions.get(0) : null;
180   }
181
182   private static boolean hasManyOwnAbstractMethods(@NotNull PsiClass psiClass) {
183     int abstractCount = 0;
184     for (PsiMethod method : psiClass.getMethods()) {
185       if (isDefinitelyAbstractInterfaceMethod(method) && ++abstractCount > 1) {
186         return true;
187       }
188     }
189     return false;
190   }
191
192   private static boolean isDefinitelyAbstractInterfaceMethod(PsiMethod method) {
193     return method.hasModifierProperty(PsiModifier.ABSTRACT) && !isPublicObjectMethod(method.getName());
194   }
195
196   private static boolean isPublicObjectMethod(String methodName) {
197     return "equals".equals(methodName) || "hashCode".equals(methodName) || "toString".equals(methodName);
198   }
199
200   private static boolean hasManyInheritedAbstractMethods(@NotNull PsiClass psiClass) {
201     final Set<String> abstractNames = new HashSet<>();
202     final Set<String> defaultNames = new HashSet<>();
203     InheritanceUtil.processSupers(psiClass, true, psiClass1 -> {
204       for (PsiMethod method : psiClass1.getMethods()) {
205         if (isDefinitelyAbstractInterfaceMethod(method)) {
206           abstractNames.add(method.getName());
207         }
208         else if (method.hasModifierProperty(PsiModifier.DEFAULT)) {
209           defaultNames.add(method.getName());
210         }
211       }
212       return true;
213     });
214     abstractNames.removeAll(defaultNames);
215     return abstractNames.size() > 1;
216   }
217
218   private static boolean overridesPublicObjectMethod(HierarchicalMethodSignature psiMethod) {
219     final List<HierarchicalMethodSignature> signatures = psiMethod.getSuperSignatures();
220     if (signatures.isEmpty()) {
221       final PsiMethod method = psiMethod.getMethod();
222       final PsiClass containingClass = method.getContainingClass();
223       if (containingClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName())) {
224         if (method.hasModifierProperty(PsiModifier.PUBLIC)) {
225           return true;
226         }
227       }
228     }
229     for (HierarchicalMethodSignature superMethod : signatures) {
230       if (overridesPublicObjectMethod(superMethod)) {
231         return true;
232       }
233     }
234     return false;
235   }
236
237   private static MethodSignature getMethodSignature(PsiMethod method, PsiClass psiClass, PsiClass containingClass) {
238     final MethodSignature methodSignature;
239     if (containingClass != null && containingClass != psiClass) {
240       methodSignature = method.getSignature(TypeConversionUtil.getSuperClassSubstitutor(containingClass, psiClass, PsiSubstitutor.EMPTY));
241     }
242     else {
243       methodSignature = method.getSignature(PsiSubstitutor.EMPTY);
244     }
245     return methodSignature;
246   }
247
248   @NotNull
249   private static List<HierarchicalMethodSignature> hasSubSignature(List<HierarchicalMethodSignature> signatures) {
250     for (HierarchicalMethodSignature signature : signatures) {
251       boolean subSignature = true;
252       for (HierarchicalMethodSignature methodSignature : signatures) {
253         if (!signature.equals(methodSignature) && !skipMethod(signature, methodSignature)) {
254           subSignature = false;
255           break;
256         }
257       }
258       if (subSignature) return Collections.singletonList(signature);
259     }
260     return signatures;
261   }
262
263   private static boolean skipMethod(HierarchicalMethodSignature signature,
264                                     HierarchicalMethodSignature methodSignature) {
265     //not generic
266     if (methodSignature.getTypeParameters().length == 0) {
267       return false;
268     }
269     //foreign class
270     return signature.getMethod().getContainingClass() != methodSignature.getMethod().getContainingClass();
271   }
272
273   @Contract("null -> null")
274   @Nullable
275   public static List<HierarchicalMethodSignature> findFunctionCandidates(@Nullable final PsiClass psiClass) {
276     if (!isPlainInterface(psiClass)) return null;
277
278     final List<HierarchicalMethodSignature> methods = new ArrayList<>();
279     final Map<MethodSignature, Set<PsiMethod>> overrideEquivalents = PsiSuperMethodUtil.collectOverrideEquivalents(psiClass);
280     final Collection<HierarchicalMethodSignature> visibleSignatures = psiClass.getVisibleSignatures();
281     for (HierarchicalMethodSignature signature : visibleSignatures) {
282       final PsiMethod psiMethod = signature.getMethod();
283       if (!psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) continue;
284       if (psiMethod.hasModifierProperty(PsiModifier.STATIC)) continue;
285       final Set<PsiMethod> equivalentMethods = overrideEquivalents.get(signature);
286       if (equivalentMethods != null && equivalentMethods.size() > 1) {
287         boolean hasNonAbstractOverrideEquivalent = false;
288         for (PsiMethod method : equivalentMethods) {
289           if (!method.hasModifierProperty(PsiModifier.ABSTRACT) && !MethodSignatureUtil.isSuperMethod(method, psiMethod)) {
290             hasNonAbstractOverrideEquivalent = true;
291             break;
292           }
293         }
294         if (hasNonAbstractOverrideEquivalent) continue;
295       }
296       if (!overridesPublicObjectMethod(signature)) {
297         methods.add(signature);
298       }
299     }
300
301     return hasSubSignature(methods);
302   }
303
304
305   @Nullable
306   private static PsiType getReturnType(PsiClass psiClass, MethodSignature methodSignature) {
307     final PsiMethod method = getMethod(psiClass, methodSignature);
308     if (method != null) {
309       final PsiClass containingClass = method.getContainingClass();
310       if (containingClass == null) return null;
311       return TypeConversionUtil.getSuperClassSubstitutor(containingClass, psiClass, PsiSubstitutor.EMPTY).substitute(method.getReturnType());
312     }
313     else {
314       return null;
315     }
316   }
317
318   @Nullable
319   private static PsiMethod getMethod(PsiClass psiClass, MethodSignature methodSignature) {
320     if (methodSignature instanceof MethodSignatureBackedByPsiMethod) {
321       return ((MethodSignatureBackedByPsiMethod)methodSignature).getMethod();
322     }
323
324     final PsiMethod[] methodsByName = psiClass.findMethodsByName(methodSignature.getName(), true);
325     for (PsiMethod psiMethod : methodsByName) {
326       if (MethodSignatureUtil
327         .areSignaturesEqual(getMethodSignature(psiMethod, psiClass, psiMethod.getContainingClass()), methodSignature)) {
328         return psiMethod;
329       }
330     }
331     return null;
332   }
333
334   public static int getLambdaIdx(PsiExpressionList expressionList, final PsiElement element) {
335     PsiExpression[] expressions = expressionList.getExpressions();
336     for (int i = 0; i < expressions.length; i++) {
337       PsiExpression expression = expressions[i];
338       if (PsiTreeUtil.isAncestor(expression, element, false)) {
339         return i;
340       }
341     }
342     return -1;
343   }
344
345   @Nullable
346   public static PsiType getFunctionalInterfaceType(PsiElement expression, final boolean tryToSubstitute) {
347     PsiElement parent = expression.getParent();
348     PsiElement element = expression;
349     while (parent instanceof PsiParenthesizedExpression || parent instanceof PsiConditionalExpression) {
350       if (parent instanceof PsiConditionalExpression && ((PsiConditionalExpression)parent).getCondition() == element) {
351         return PsiType.BOOLEAN;
352       }
353       element = parent;
354       parent = parent.getParent();
355     }
356
357     PsiType type = ThreadLocalTypes.getElementType(expression);
358     if (type == null) type = ThreadLocalTypes.getElementType(element);
359     if (type != null) {
360       return type;
361     }
362
363     if (parent instanceof PsiArrayInitializerExpression) {
364       final PsiType psiType = ((PsiArrayInitializerExpression)parent).getType();
365       if (psiType instanceof PsiArrayType) {
366         return ((PsiArrayType)psiType).getComponentType();
367       }
368     }
369     else if (parent instanceof PsiTypeCastExpression) {
370       // Ensure no capture is performed to target type of cast expression, from 15.16 "Cast Expressions":
371       // Casts can be used to explicitly "tag" a lambda expression or a method reference expression with a particular target type.
372       // To provide an appropriate degree of flexibility, the target type may be a list of types denoting an intersection type,
373       // provided the intersection induces a functional interface (p9.8).
374       final PsiTypeElement castTypeElement = ((PsiTypeCastExpression)parent).getCastType();
375       final PsiType castType = castTypeElement != null ? castTypeElement.getType() : null;
376       if (castType instanceof PsiIntersectionType) {
377         final PsiType conjunct = extractFunctionalConjunct((PsiIntersectionType)castType);
378         if (conjunct != null) return conjunct;
379       }
380       return castType;
381     }
382     else if (parent instanceof PsiVariable) {
383       return ((PsiVariable)parent).getType();
384     }
385     else if (parent instanceof PsiAssignmentExpression && expression instanceof PsiExpression && !PsiUtil.isOnAssignmentLeftHand((PsiExpression)expression)) {
386       final PsiExpression lExpression = ((PsiAssignmentExpression)parent).getLExpression();
387       return lExpression.getType();
388     }
389     else if (parent instanceof PsiExpressionList) {
390       final PsiExpressionList expressionList = (PsiExpressionList)parent;
391       final int lambdaIdx = getLambdaIdx(expressionList, expression);
392       if (lambdaIdx >= 0) {
393         PsiElement gParent = expressionList.getParent();
394
395         if (gParent instanceof PsiAnonymousClass) {
396           gParent = gParent.getParent();
397         }
398
399         if (gParent instanceof PsiCall) {
400           final PsiCall contextCall = (PsiCall)gParent;
401           LOG.assertTrue(!MethodCandidateInfo.isOverloadCheck(contextCall.getArgumentList()));
402           JavaResolveResult resolveResult = PsiDiamondType.getDiamondsAwareResolveResult(contextCall);
403           return getSubstitutedType(expression, tryToSubstitute, lambdaIdx, resolveResult);
404         }
405       }
406     }
407     else if (parent instanceof PsiReturnStatement) {
408       return PsiTypesUtil.getMethodReturnType(parent);
409     }
410     else if (parent instanceof PsiLambdaExpression) {
411       return getFunctionalInterfaceTypeByContainingLambda((PsiLambdaExpression)parent);
412     }
413     PsiSwitchExpression switchExpression = PsiTreeUtil.getParentOfType(element, PsiSwitchExpression.class);
414     if (switchExpression != null && PsiUtil.getSwitchResultExpressions(switchExpression).contains(element)) {
415       return getFunctionalInterfaceType(switchExpression, tryToSubstitute);
416     }
417     return null;
418   }
419
420   @Nullable
421   private static PsiType getSubstitutedType(PsiElement expression,
422                                             boolean tryToSubstitute,
423                                             int lambdaIdx,
424                                             final JavaResolveResult resolveResult) {
425     final PsiElement resolve = resolveResult.getElement();
426     if (resolve instanceof PsiMethod) {
427       final PsiParameter[] parameters = ((PsiMethod)resolve).getParameterList().getParameters();
428       final int finalLambdaIdx = adjustLambdaIdx(lambdaIdx, (PsiMethod)resolve, parameters);
429       if (finalLambdaIdx < parameters.length) {
430         if (!tryToSubstitute) return getNormalizedType(parameters[finalLambdaIdx]);
431         return PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, !MethodCandidateInfo.isOverloadCheck(), () -> {
432           final PsiType normalizedType = getNormalizedType(parameters[finalLambdaIdx]);
433           if (resolveResult instanceof MethodCandidateInfo && ((MethodCandidateInfo)resolveResult).isRawSubstitution()) {
434             return TypeConversionUtil.erasure(normalizedType);
435           }
436           else {
437             return resolveResult.getSubstitutor().substitute(normalizedType);
438           }
439         });
440       }
441     }
442     return null;
443   }
444
445   public static boolean processParentOverloads(PsiFunctionalExpression functionalExpression, final Consumer<? super PsiType> overloadProcessor) {
446     LOG.assertTrue(PsiTypesUtil.getExpectedTypeByParent(functionalExpression) == null);
447     PsiElement parent = functionalExpression.getParent();
448     PsiElement expr = functionalExpression;
449     while (parent instanceof PsiParenthesizedExpression || parent instanceof PsiConditionalExpression) {
450       if (parent instanceof PsiConditionalExpression &&
451           ((PsiConditionalExpression)parent).getThenExpression() != expr &&
452           ((PsiConditionalExpression)parent).getElseExpression() != expr) break;
453       expr = parent;
454       parent = parent.getParent();
455     }
456     if (parent instanceof PsiExpressionList) {
457       final PsiExpressionList expressionList = (PsiExpressionList)parent;
458       final int lambdaIdx = getLambdaIdx(expressionList, functionalExpression);
459       if (lambdaIdx > -1) {
460
461         PsiElement gParent = expressionList.getParent();
462
463         if (gParent instanceof PsiAnonymousClass) {
464           gParent = gParent.getParent();
465         }
466
467         JavaResolveResult[] results = null;
468         if (gParent instanceof PsiMethodCallExpression) {
469           results = ((PsiMethodCallExpression)gParent).getMethodExpression().multiResolve(true);
470         }
471         else if (gParent instanceof PsiConstructorCall){
472           results = getConstructorCandidates((PsiConstructorCall)gParent);
473         }
474
475         if (results != null) {
476           final Set<PsiType> types = new HashSet<>();
477           for (JavaResolveResult result : results) {
478             final PsiType functionalExpressionType = MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(functionalExpression, false, () -> getSubstitutedType(functionalExpression, true, lambdaIdx, result));
479             if (functionalExpressionType != null && types.add(functionalExpressionType)) {
480               overloadProcessor.consume(functionalExpressionType);
481             }
482           }
483           return true;
484         }
485       }
486     }
487     return false;
488   }
489
490   @Nullable
491   private static JavaResolveResult[] getConstructorCandidates(PsiConstructorCall parentCall) {
492     final JavaPsiFacade facade = JavaPsiFacade.getInstance(parentCall.getProject());
493     PsiExpressionList argumentList = parentCall.getArgumentList();
494     if (argumentList != null) {
495       PsiClassType classType = null;
496       if (parentCall instanceof PsiNewExpression) {
497         PsiJavaCodeReferenceElement ref = ((PsiNewExpression)parentCall).getClassReference();
498         classType = ref != null ? facade.getElementFactory().createType(ref) : null;
499       }
500       else if (parentCall instanceof PsiEnumConstant) {
501         PsiClass containingClass = ((PsiEnumConstant)parentCall).getContainingClass();
502         classType = containingClass != null ? facade.getElementFactory().createType(containingClass) : null;
503       }
504
505       if (classType != null) {
506         return facade.getResolveHelper().multiResolveConstructor(classType, argumentList, parentCall);
507       }
508     }
509     return null;
510   }
511
512   @Nullable
513   private static PsiType extractFunctionalConjunct(PsiIntersectionType type) {
514     PsiType conjunct = null;
515     MethodSignature commonSignature = null;
516     for (PsiType psiType : type.getConjuncts()) {
517       PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(psiType);
518       if (aClass instanceof PsiTypeParameter) continue;
519       MethodSignature signature = getFunction(aClass);
520       if (signature == null) continue;
521       if (commonSignature == null) {
522         commonSignature = signature;
523       }
524       else if (!MethodSignatureUtil.areSignaturesEqual(commonSignature, signature)) {
525         return null;
526       }
527       conjunct = psiType;
528     }
529
530     return conjunct;
531   }
532
533   private static PsiType getFunctionalInterfaceTypeByContainingLambda(@NotNull PsiLambdaExpression parentLambda) {
534     final PsiType parentInterfaceType = parentLambda.getFunctionalInterfaceType();
535     return parentInterfaceType != null ? getFunctionalInterfaceReturnType(parentInterfaceType) : null;
536   }
537
538   private static int adjustLambdaIdx(int lambdaIdx, PsiMethod resolve, PsiParameter[] parameters) {
539     final int finalLambdaIdx;
540     if (resolve.isVarArgs() && lambdaIdx >= parameters.length) {
541       finalLambdaIdx = parameters.length - 1;
542     } else {
543       finalLambdaIdx = lambdaIdx;
544     }
545     return finalLambdaIdx;
546   }
547
548   private static PsiType getNormalizedType(PsiParameter parameter) {
549     final PsiType type = parameter.getType();
550     if (type instanceof PsiEllipsisType) {
551       return ((PsiEllipsisType)type).getComponentType();
552     }
553     return type;
554   }
555
556   @Contract(value = "null -> false", pure = true)
557   public static boolean notInferredType(PsiType typeByExpression) {
558     return typeByExpression instanceof PsiMethodReferenceType ||
559            typeByExpression instanceof PsiLambdaExpressionType ||
560            typeByExpression instanceof PsiLambdaParameterType;
561   }
562
563   @NotNull
564   public static PsiReturnStatement[] getReturnStatements(PsiLambdaExpression lambdaExpression) {
565     final PsiElement body = lambdaExpression.getBody();
566     return body instanceof PsiCodeBlock ? PsiUtil.findReturnStatements((PsiCodeBlock)body) : PsiReturnStatement.EMPTY_ARRAY;
567   }
568
569   public static List<PsiExpression> getReturnExpressions(PsiLambdaExpression lambdaExpression) {
570     final PsiElement body = lambdaExpression.getBody();
571     if (body instanceof PsiExpression) {
572       //if (((PsiExpression)body).getType() != PsiType.VOID) return Collections.emptyList();
573       return Collections.singletonList((PsiExpression)body);
574     }
575     final List<PsiExpression> result = new ArrayList<>();
576     for (PsiReturnStatement returnStatement : getReturnStatements(lambdaExpression)) {
577       final PsiExpression returnValue = returnStatement.getReturnValue();
578       if (returnValue != null) {
579         result.add(returnValue);
580       }
581     }
582     return result;
583   }
584
585   //JLS 14.8 Expression Statements
586   @Contract("null -> false")
587   public static boolean isExpressionStatementExpression(PsiElement body) {
588     return body instanceof PsiAssignmentExpression ||
589            PsiUtil.isIncrementDecrementOperation(body) ||
590            body instanceof PsiMethodCallExpression || //method invocation
591            body instanceof PsiNewExpression && !((PsiNewExpression)body).isArrayCreation() || //class instance creation
592            body instanceof PsiReferenceExpression && !body.isPhysical();
593   }
594
595   public static PsiExpression extractSingleExpressionFromBody(PsiElement body) {
596     PsiExpression expression = null;
597     if (body instanceof PsiExpression) {
598       expression = (PsiExpression)body;
599     }
600     else if (body instanceof PsiCodeBlock) {
601       final PsiStatement[] statements = ((PsiCodeBlock)body).getStatements();
602       if (statements.length == 1) {
603         if (statements[0] instanceof PsiReturnStatement) {
604           expression = ((PsiReturnStatement)statements[0]).getReturnValue();
605         }
606         else if (statements[0] instanceof PsiExpressionStatement) {
607           expression = ((PsiExpressionStatement)statements[0]).getExpression();
608         }
609         else if (statements[0] instanceof PsiBlockStatement) {
610           return extractSingleExpressionFromBody(((PsiBlockStatement)statements[0]).getCodeBlock());
611         }
612       }
613     }
614     else if (body instanceof PsiBlockStatement) {
615       return extractSingleExpressionFromBody(((PsiBlockStatement)body).getCodeBlock());
616     }
617     else if (body instanceof PsiExpressionStatement) {
618       expression = ((PsiExpressionStatement)body).getExpression();
619     }
620     return expression;
621   }
622
623   // http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.1
624   // A lambda expression or a method reference expression is potentially compatible with a type variable
625   // if the type variable is a type parameter of the candidate method.
626   public static boolean isPotentiallyCompatibleWithTypeParameter(PsiFunctionalExpression expression,
627                                                                  PsiExpressionList argsList,
628                                                                  PsiMethod method) {
629     if (!Registry.is("JDK8042508.bug.fixed", false)) {
630       final PsiCallExpression callExpression = PsiTreeUtil.getParentOfType(argsList, PsiCallExpression.class);
631       if (callExpression == null || callExpression.getTypeArguments().length > 0) {
632         return false;
633       }
634     }
635
636     final int lambdaIdx = getLambdaIdx(argsList, expression);
637     if (lambdaIdx >= 0) {
638       final PsiParameter[] parameters = method.getParameterList().getParameters();
639       final PsiParameter lambdaParameter = parameters[Math.min(lambdaIdx, parameters.length - 1)];
640       final PsiClass paramClass = PsiUtil.resolveClassInType(lambdaParameter.getType());
641       if (paramClass instanceof PsiTypeParameter && ((PsiTypeParameter)paramClass).getOwner() == method) {
642         return true;
643       }
644     }
645     return false;
646   }
647
648   public static Map<PsiElement, String> checkReturnTypeCompatible(PsiLambdaExpression lambdaExpression, PsiType functionalInterfaceReturnType) {
649     Map<PsiElement, String> errors = new LinkedHashMap<>();
650     if (PsiType.VOID.equals(functionalInterfaceReturnType)) {
651       final PsiElement body = lambdaExpression.getBody();
652       if (body instanceof PsiCodeBlock) {
653         for (PsiExpression expression : getReturnExpressions(lambdaExpression)) {
654           errors.put(expression, "Unexpected return value");
655         }
656       }
657       else if (body instanceof PsiExpression) {
658         final PsiType type = ((PsiExpression)body).getType();
659         try {
660           if (!PsiUtil.isStatement(JavaPsiFacade.getElementFactory(body.getProject()).createStatementFromText(body.getText(), body))) {
661             if (PsiType.VOID.equals(type)) {
662               errors.put(body, "Lambda body must be a statement expression");
663             }
664             else {
665               errors.put(body, "Bad return type in lambda expression: " + (type == PsiType.NULL || type == null ? "<null>" : type.getPresentableText()) + " cannot be converted to void");
666             }
667           }
668         }
669         catch (IncorrectOperationException ignore) {
670         }
671       }
672     }
673     else if (functionalInterfaceReturnType != null) {
674       final List<PsiExpression> returnExpressions = getReturnExpressions(lambdaExpression);
675       for (final PsiExpression expression : returnExpressions) {
676         final PsiType expressionType = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, true, expression::getType);
677         if (expressionType != null && !functionalInterfaceReturnType.isAssignableFrom(expressionType)) {
678           errors.put(expression, "Bad return type in lambda expression: " + expressionType.getPresentableText() + " cannot be converted to " + functionalInterfaceReturnType.getPresentableText());
679         }
680       }
681       final PsiReturnStatement[] returnStatements = getReturnStatements(lambdaExpression);
682       if (returnStatements.length > returnExpressions.size()) {
683         for (PsiReturnStatement statement : returnStatements) {
684           final PsiExpression value = statement.getReturnValue();
685           if (value == null) {
686             errors.put(statement, "Missing return value");
687           }
688         }
689       }
690       else if (returnExpressions.isEmpty() && !lambdaExpression.isVoidCompatible()) {
691         errors.put(lambdaExpression, "Missing return value");
692       }
693     }
694     return errors.isEmpty() ? null : errors;
695   }
696
697   @Nullable
698   public static PsiType getLambdaParameterFromType(PsiType functionalInterfaceType, int parameterIndex) {
699     final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
700     final PsiMethod method = getFunctionalInterfaceMethod(functionalInterfaceType);
701     if (method != null) {
702       final PsiParameter[] parameters = method.getParameterList().getParameters();
703       if (parameterIndex < parameters.length) {
704         return getSubstitutor(method, resolveResult).substitute(parameters[parameterIndex].getType());
705       }
706     }
707     return null;
708   }
709
710   @Nullable
711   public static PsiCall treeWalkUp(PsiElement context) {
712     PsiCall top = null;
713     PsiElement parent = PsiTreeUtil.getParentOfType(context,
714                                                     PsiExpressionList.class,
715                                                     PsiLambdaExpression.class,
716                                                     PsiConditionalExpression.class,
717                                                     PsiSwitchExpression.class,
718                                                     PsiAssignmentExpression.class,
719                                                     PsiCodeBlock.class,
720                                                     PsiCall.class);
721     while (true) {
722       if (parent instanceof PsiCall || parent instanceof PsiAssignmentExpression) {
723         break;
724       }
725
726       final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class);
727       if (parent instanceof PsiCodeBlock) {
728         if (lambdaExpression == null) {
729           break;
730         }
731         else {
732           boolean inReturnExpressions = false;
733           for (PsiExpression expression : getReturnExpressions(lambdaExpression)) {
734             inReturnExpressions |= PsiTreeUtil.isAncestor(expression, context, false);
735           }
736
737           if (!inReturnExpressions) {
738             break;
739           }
740
741           if (ThreadLocalTypes.hasBindingFor(lambdaExpression)) {
742             break;
743           }
744         }
745       }
746
747       if ((parent instanceof PsiConditionalExpression || parent instanceof PsiSwitchExpression) && !PsiPolyExpressionUtil.isPolyExpression((PsiExpression)parent)) {
748         break;
749       }
750
751       if (parent instanceof PsiLambdaExpression && ThreadLocalTypes.hasBindingFor(parent)) {
752         break;
753       }
754
755       final PsiCall psiCall = PsiTreeUtil.getParentOfType(parent, PsiCall.class, false, PsiMember.class, PsiVariable.class);
756       if (psiCall == null) {
757         break;
758       }
759       if (MethodCandidateInfo.isOverloadCheck(psiCall.getArgumentList()) ||
760           lambdaExpression != null && ThreadLocalTypes.hasBindingFor(lambdaExpression)) {
761         break;
762       }
763
764       top = psiCall;
765       if (top instanceof PsiExpression && PsiPolyExpressionUtil.isPolyExpression((PsiExpression)top)) {
766         parent = PsiTreeUtil.getParentOfType(parent.getParent(), PsiExpressionList.class, PsiLambdaExpression.class, PsiAssignmentExpression.class, PsiCodeBlock.class);
767       }
768       else {
769         break;
770       }
771     }
772
773     if (top == null) {
774       return null;
775     }
776
777     final PsiExpressionList argumentList = top.getArgumentList();
778     if (argumentList == null) {
779       return null;
780     }
781
782     LOG.assertTrue(!MethodCandidateInfo.isOverloadCheck(argumentList));
783     return top;
784   }
785
786   public static PsiCall copyTopLevelCall(@NotNull PsiCall call) {
787     if (call instanceof PsiEnumConstant) {
788       PsiClass containingClass = ((PsiEnumConstant)call).getContainingClass();
789       if (containingClass == null) {
790         return null;
791       }
792       String enumName = containingClass.getName();
793       if (enumName == null) {
794         return null;
795       }
796       PsiMethod resolveMethod = call.resolveMethod();
797       if (resolveMethod == null) {
798         return null;
799       }
800       PsiElement contextFile = call.getContainingFile().copy();
801       PsiClass anEnum = (PsiClass)contextFile.add(JavaPsiFacade.getElementFactory(call.getProject()).createEnum(enumName));
802       anEnum.add(resolveMethod);
803       return  (PsiCall)anEnum.add(call);
804     }
805     PsiElement expressionForType = call;
806     while (true) {
807       PsiElement parent = PsiUtil.skipParenthesizedExprUp(expressionForType.getParent());
808       if (!(parent instanceof PsiConditionalExpression) ||
809           PsiTreeUtil.isAncestor(((PsiConditionalExpression)parent).getCondition(), expressionForType, false)) {
810         break;
811       }
812       expressionForType = parent;
813     }
814     PsiType type = PsiTypesUtil.getExpectedTypeByParent(expressionForType);
815     if (type != null && PsiTypesUtil.isDenotableType(type, call)) {
816       return (PsiCall)copyWithExpectedType(call, type);
817     }
818     return (PsiCall)call.copy();
819   }
820
821   public static <T> T performWithSubstitutedParameterBounds(final PsiTypeParameter[] typeParameters,
822                                                             final PsiSubstitutor substitutor,
823                                                             final Supplier<? extends T> producer) {
824     return ThreadLocalTypes.performWithTypes(map -> {
825       for (PsiTypeParameter parameter : typeParameters) {
826         final PsiClassType[] types = parameter.getExtendsListTypes();
827         if (types.length > 0) {
828           final List<PsiType> conjuncts = ContainerUtil.map(types, substitutor::substitute);
829           //don't glb to avoid flattening = Object&Interface would be preserved
830           //otherwise methods with different signatures could get same erasure
831           final PsiType upperBound = PsiIntersectionType.createIntersection(false, conjuncts.toArray(PsiType.EMPTY_ARRAY));
832           map.forceType(parameter, upperBound);
833         }
834       }
835       return producer.get();
836     });
837   }
838
839   public static <T> T performWithTargetType(@NotNull PsiElement element, @NotNull PsiType targetType, @NotNull Supplier<? extends T> producer) {
840     return ThreadLocalTypes.performWithTypes(types -> {
841       types.forceType(element, targetType);
842       return producer.get();
843     });
844   }
845
846   /**
847    * Generate lambda text for single argument expression lambda
848    *
849    * @param variable lambda sole argument
850    * @param expression lambda body (expression)
851    * @return lambda text
852    */
853   public static String createLambda(@NotNull PsiVariable variable, @NotNull PsiExpression expression) {
854     return variable.getName() + " -> " + expression.getText();
855   }
856
857   /**
858    * Returns true if lambda has single parameter and its return value is the same as parameter.
859    *
860    * <p>
861    * The lambdas like this are considered identity lambda: {@code x -> x}, {@code x -> {return x;}}
862    * {@code (String x) -> (x)}, etc.</p>
863    *
864    * <p>
865    * This method does not check the lambda type, also it does not check whether auto-(un)boxing occurs,
866    * so a lambda like {@code ((Predicate<Boolean>)b -> b)} is also identity lambda even though it performs
867    * auto-unboxing.
868    * </p>
869    *
870    * @param lambda a lambda to check
871    * @return true if the supplied lambda is an identity lambda
872    */
873   public static boolean isIdentityLambda(PsiLambdaExpression lambda) {
874     PsiParameterList parameters = lambda.getParameterList();
875     if (parameters.getParametersCount() != 1) return false;
876     PsiExpression expression = PsiUtil.skipParenthesizedExprDown(extractSingleExpressionFromBody(lambda.getBody()));
877     return expression instanceof PsiReferenceExpression &&
878            ((PsiReferenceExpression)expression).isReferenceTo(parameters.getParameters()[0]);
879   }
880
881   private static boolean isSafeLambdaReplacement(@NotNull PsiLambdaExpression lambda,
882                                                  @NotNull Function<? super PsiLambdaExpression, ? extends PsiExpression> replacer) {
883     PsiElement body = lambda.getBody();
884     if (body == null) return false;
885     final PsiCall call = treeWalkUp(body);
886     if (call != null) {
887       JavaResolveResult result = call.resolveMethodGenerics();
888       PsiElement oldTarget = result.getElement();
889       if (oldTarget != null) {
890         String origErrorMessage = result instanceof MethodCandidateInfo ? ((MethodCandidateInfo)result).getInferenceErrorMessage() : null;
891         Object marker = new Object();
892         PsiTreeUtil.mark(lambda, marker);
893         PsiType origType = call instanceof PsiExpression ? ((PsiExpression)call).getType() : null;
894         PsiCall copyCall = copyTopLevelCall(call);
895         if (copyCall == null) return false;
896         PsiLambdaExpression lambdaCopy = ObjectUtils.tryCast(PsiTreeUtil.releaseMark(copyCall, marker), PsiLambdaExpression.class);
897         if (lambdaCopy == null) return false;
898         PsiExpression function = replacer.apply(lambdaCopy);
899         if (function == null) return false;
900         JavaResolveResult resultCopy = copyCall.resolveMethodGenerics();
901         if (resultCopy.getElement() != oldTarget) return false;
902         String copyMessage = resultCopy instanceof MethodCandidateInfo ? ((MethodCandidateInfo)resultCopy).getInferenceErrorMessage() : null;
903         if (!Objects.equals(origErrorMessage, copyMessage)) return false;
904         if (function instanceof PsiFunctionalExpression && ((PsiFunctionalExpression)function).getFunctionalInterfaceType() == null) {
905           return false;
906         }
907         PsiType newType = copyCall instanceof PsiExpression ? ((PsiExpression)copyCall).getType() : null;
908         if (origType instanceof PsiClassType && newType instanceof PsiClassType &&
909             ((PsiClassType)origType).isRaw() != ((PsiClassType)newType).isRaw()) {
910           return false;
911         }
912       }
913     }
914     return true;
915   }
916
917   /**
918    * Returns false if after suggested replacement of lambda body, containing method call would resolve to something else
919    * or its return type will change.
920    *
921    * @param lambda              a lambda whose body is going to be replaced
922    * @param newFunctionSupplier replacement for lambda to check,
923    *                            lazy computed for lambdas in invocation context only.
924    *                            Replacement evaluated to {@code null} is treated as invalid overload
925    */
926   public static boolean isSafeLambdaReplacement(@NotNull PsiLambdaExpression lambda, @NotNull Supplier<? extends PsiExpression> newFunctionSupplier) {
927     return isSafeLambdaReplacement(lambda, l -> {
928       PsiExpression replacement = newFunctionSupplier.get();
929       return replacement == null ? null : (PsiExpression)l.replace(replacement);
930     });
931   }
932
933   /**
934    * Returns false if after suggested replacement of lambda body, containing method call would resolve to something else
935    * or its return type will change.
936    *
937    * @param lambda          a lambda whose body is going to be replaced
938    * @param replacementText a text of new expression to replace lambda
939    */
940   public static boolean isSafeLambdaReplacement(@NotNull PsiLambdaExpression lambda, @NotNull String replacementText) {
941     return isSafeLambdaReplacement(lambda, () -> JavaPsiFacade.getElementFactory(lambda.getProject())
942       .createExpressionFromText(replacementText, lambda.getParent()));
943   }
944
945   /**
946    * Returns false if after suggested replacement of lambda body, containing method call would resolve to something else
947    * or its return type will change.
948    *
949    * <p>
950    * True will be returned for lambdas in non-invocation context as well as for lambdas in invocation context,
951    * when invoked method is not overloaded or all overloads are 'lambda friendly'
952    *
953    * <p>
954    *   Value-compatible lambda like {@code () -> foo() == true} can be converted to value-compatible AND void-compatible
955    *   {@code () -> foo()} during simplification. This could lead to ambiguity during containing method call resolution and thus
956    *   to the errors after applying the suggestion.
957    * </p>
958    *
959    * @param lambda          a lambda whose body is going to be replaced
960    * @param newBodySupplier replacement for lambda's body to check,
961    *                        lazy computed for lambdas in invocation context only.
962    *                        Replacement evaluated to {@code null} is treated as invalid overload
963    */
964   public static boolean isSafeLambdaBodyReplacement(@NotNull PsiLambdaExpression lambda, @NotNull Supplier<? extends PsiElement> newBodySupplier) {
965     return isSafeLambdaReplacement(lambda, l -> {
966       PsiElement oldBody = l.getBody();
967       PsiElement newBody = newBodySupplier.get();
968       if (oldBody == null || newBody == null) return null;
969       oldBody.replace(newBody);
970       return l;
971     });
972   }
973
974   /**
975    * {@link #isSafeLambdaBodyReplacement(PsiLambdaExpression, Supplier)} overload to test the same lambda body,
976    * but with only return value {@code expression} changed to {@code replacement}
977    * @param lambdaReturnExpression a return expression inside lambda body
978    * @param replacement a replacement for return expression
979    */
980   public static boolean isSafeLambdaReturnValueReplacement(@NotNull PsiExpression lambdaReturnExpression,
981                                                            @NotNull PsiExpression replacement) {
982     if (lambdaReturnExpression.getParent() instanceof PsiReturnStatement || lambdaReturnExpression.getParent() instanceof PsiLambdaExpression) {
983       PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(lambdaReturnExpression, PsiLambdaExpression.class, true, PsiMethod.class);
984       if (lambdaExpression != null &&
985           !isSafeLambdaBodyReplacement(lambdaExpression, () -> {
986             PsiLambdaExpression lambdaExpression1 = PsiTreeUtil.getParentOfType(lambdaReturnExpression, PsiLambdaExpression.class);
987             if (lambdaExpression1 == null) return null;
988             PsiElement body = lambdaExpression1.getBody();
989             if (body == null) return null;
990             Object marker = new Object();
991             PsiTreeUtil.mark(lambdaReturnExpression, marker);
992             PsiElement copy = body.copy();
993             PsiElement exprInReturn = PsiTreeUtil.releaseMark(copy, marker);
994             if (exprInReturn == null) return null;
995             if (exprInReturn == copy) {
996               return exprInReturn.replace(replacement);
997             }
998             exprInReturn.replace(replacement);
999             return copy;
1000           })) {
1001         return false;
1002       }
1003     }
1004     return true;
1005   }
1006
1007   public static PsiElement copyWithExpectedType(PsiElement expression, PsiType type) {
1008     String canonicalText = type.getCanonicalText();
1009     if (!PsiUtil.isLanguageLevel8OrHigher(expression)) {
1010       final String arrayInitializer = "new " + canonicalText + "[]{0}";
1011       PsiNewExpression newExpr = (PsiNewExpression)createExpressionFromText(arrayInitializer, expression);
1012       final PsiArrayInitializerExpression initializer = newExpr.getArrayInitializer();
1013       LOG.assertTrue(initializer != null);
1014       return initializer.getInitializers()[0].replace(expression);
1015     }
1016
1017     final String callableWithExpectedType = "(java.util.concurrent.Callable<" + canonicalText + ">)() -> x";
1018     PsiTypeCastExpression typeCastExpr = (PsiTypeCastExpression)createExpressionFromText(callableWithExpectedType, expression);
1019     PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)typeCastExpr.getOperand();
1020     LOG.assertTrue(lambdaExpression != null);
1021     PsiElement body = lambdaExpression.getBody();
1022     LOG.assertTrue(body instanceof PsiExpression);
1023     return body.replace(expression);
1024   }
1025
1026   private static PsiExpression createExpressionFromText(String exprText, PsiElement context) {
1027     PsiExpression expr = JavaPsiFacade.getElementFactory(context.getProject())
1028                                       .createExpressionFromText(exprText, context);
1029     //ensure refs to inner classes are collapsed to avoid raw types (container type would be raw in qualified text)
1030     return (PsiExpression)JavaCodeStyleManager.getInstance(context.getProject()).shortenClassReferences(expr);
1031   }
1032
1033   /**
1034    * Returns true if given lambda captures any variable or "this" reference.
1035    * @param lambda lambda to check
1036    * @return true if given lambda captures any variable or "this" reference.
1037    */
1038   public static boolean isCapturingLambda(PsiLambdaExpression lambda) {
1039     PsiElement body = lambda.getBody();
1040     if (body == null) return false;
1041     class CapturingLambdaVisitor extends JavaRecursiveElementWalkingVisitor {
1042       boolean capturing;
1043
1044       @Override
1045       public void visitReferenceExpression(PsiReferenceExpression expression) {
1046         super.visitReferenceExpression(expression);
1047         if (expression instanceof PsiMethodReferenceExpression) return;
1048         if (expression.getParent() instanceof PsiMethodCallExpression && expression.getQualifierExpression() != null) return;
1049         PsiElement target = expression.resolve();
1050         // variable/parameter/field/method/class are always PsiModifierListOwners
1051         if (target instanceof PsiModifierListOwner &&
1052             !((PsiModifierListOwner)target).hasModifierProperty(PsiModifier.STATIC) &&
1053             !PsiTreeUtil.isAncestor(lambda, target, true)) {
1054           if (target instanceof PsiClass && ((PsiClass)target).getContainingClass() == null) return;
1055           capturing = true;
1056           stopWalking();
1057         }
1058       }
1059
1060       @Override
1061       public void visitSuperExpression(PsiSuperExpression expression) {
1062         capturing = true;
1063         stopWalking();
1064       }
1065
1066       @Override
1067       public void visitThisExpression(PsiThisExpression expression) {
1068         capturing = true;
1069         stopWalking();
1070       }
1071     }
1072     CapturingLambdaVisitor visitor = new CapturingLambdaVisitor();
1073     body.accept(visitor);
1074     return visitor.capturing;
1075   }
1076
1077   /**
1078    * Resolves a functional interface class for given functional expression
1079    *
1080    * @param expression functional expression
1081    * @return resolved class or null if cannot be resolved
1082    */
1083   @Nullable
1084   public static PsiClass resolveFunctionalInterfaceClass(@NotNull PsiFunctionalExpression expression) {
1085     // First try to avoid substitution
1086     PsiType type = expression.getGroundTargetType(getFunctionalInterfaceType(expression, false));
1087     PsiClass actualClass = PsiUtil.resolveClassInClassTypeOnly(type);
1088     if (actualClass instanceof PsiTypeParameter) {
1089       // Rare case when function is resolved to a type parameter: perform substitution then
1090       return PsiUtil.resolveClassInClassTypeOnly(expression.getFunctionalInterfaceType());
1091     }
1092     return actualClass;
1093   }
1094 }