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