new overload resolution: integrate isPotentiallyCompatible in isApplicable checks
[idea/community.git] / java / java-psi-api / src / com / intellij / psi / LambdaUtil.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.psi;
17
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.util.Comparing;
20 import com.intellij.openapi.util.Computable;
21 import com.intellij.openapi.util.registry.Registry;
22 import com.intellij.pom.java.LanguageLevel;
23 import com.intellij.psi.infos.MethodCandidateInfo;
24 import com.intellij.psi.util.*;
25 import org.jetbrains.annotations.Contract;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28
29 import java.util.*;
30
31 /**
32  * User: anna
33  * Date: 7/17/12
34  */
35 public class LambdaUtil {
36   public static ThreadLocal<Map<PsiElement, PsiType>> ourFunctionTypes = new ThreadLocal<Map<PsiElement, PsiType>>();
37   private static final Logger LOG = Logger.getInstance("#" + LambdaUtil.class.getName());
38
39   @Nullable
40   public static PsiType getFunctionalInterfaceReturnType(PsiLambdaExpression expr) {
41     return getFunctionalInterfaceReturnType(expr.getFunctionalInterfaceType());
42   }
43
44   @Nullable
45   public static PsiType getFunctionalInterfaceReturnType(@Nullable PsiType functionalInterfaceType) {
46     final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
47     final PsiClass psiClass = resolveResult.getElement();
48     if (psiClass != null) {
49       final MethodSignature methodSignature = getFunction(psiClass);
50       if (methodSignature != null) {
51         final PsiType returnType = getReturnType(psiClass, methodSignature);
52         return resolveResult.getSubstitutor().substitute(returnType);
53       }
54     }
55     return null;
56   }
57
58   @Nullable
59   public static PsiMethod getFunctionalInterfaceMethod(@Nullable PsiType functionalInterfaceType) {
60     return getFunctionalInterfaceMethod(PsiUtil.resolveGenericsClassInType(functionalInterfaceType));
61   }
62
63   public static PsiMethod getFunctionalInterfaceMethod(@Nullable PsiElement element) {
64     if (element instanceof PsiFunctionalExpression) {
65       final PsiType samType = ((PsiFunctionalExpression)element).getFunctionalInterfaceType();
66       return getFunctionalInterfaceMethod(samType);
67     }
68     return null;
69   }
70
71   @Nullable
72   public static PsiMethod getFunctionalInterfaceMethod(PsiClassType.ClassResolveResult result) {
73     return getFunctionalInterfaceMethod(result.getElement());
74   }
75
76   @Nullable
77   public static PsiMethod getFunctionalInterfaceMethod(PsiClass aClass) {
78     final MethodSignature methodSignature = getFunction(aClass);
79     if (methodSignature != null) {
80       return getMethod(aClass, methodSignature);
81     }
82     return null;
83   }
84
85   public static PsiSubstitutor getSubstitutor(@NotNull PsiMethod method, @NotNull PsiClassType.ClassResolveResult resolveResult) {
86     final PsiClass derivedClass = resolveResult.getElement();
87     LOG.assertTrue(derivedClass != null);
88
89     final PsiClass methodContainingClass = method.getContainingClass();
90     LOG.assertTrue(methodContainingClass != null);
91     PsiSubstitutor initialSubst = resolveResult.getSubstitutor();
92     final PsiSubstitutor superClassSubstitutor =
93       TypeConversionUtil.getSuperClassSubstitutor(methodContainingClass, derivedClass, PsiSubstitutor.EMPTY);
94     for (PsiTypeParameter param : superClassSubstitutor.getSubstitutionMap().keySet()) {
95       final PsiType substitute = superClassSubstitutor.substitute(param);
96       if (substitute != null) {
97         initialSubst = initialSubst.put(param, initialSubst.substitute(substitute));
98       }
99     }
100     return initialSubst;
101   }
102
103   public static boolean isFunctionalType(PsiType type) {
104     if (type instanceof PsiIntersectionType) {
105       return extractFunctionalConjunct((PsiIntersectionType)type) != null;
106     }
107     return isFunctionalClass(PsiUtil.resolveGenericsClassInType(type).getElement());
108   }
109
110   @Contract("null -> false")
111   public static boolean isFunctionalClass(PsiClass aClass) {
112     if (aClass != null) {
113       if (aClass instanceof PsiTypeParameter) return false;
114       final List<HierarchicalMethodSignature> signatures = findFunctionCandidates(aClass);
115       return signatures != null && signatures.size() == 1;
116     }
117     return false;
118   }
119
120   public static boolean isValidLambdaContext(@Nullable PsiElement context) {
121     return context instanceof PsiTypeCastExpression ||
122            context instanceof PsiAssignmentExpression ||
123            context instanceof PsiVariable ||
124            context instanceof PsiLambdaExpression ||
125            context instanceof PsiReturnStatement ||
126            context instanceof PsiExpressionList ||
127            context instanceof PsiParenthesizedExpression ||
128            context instanceof PsiArrayInitializerExpression ||
129            context instanceof PsiConditionalExpression && PsiTreeUtil.getParentOfType(context, PsiTypeCastExpression.class) == null;
130   }
131
132   public static boolean isLambdaFullyInferred(PsiLambdaExpression expression, PsiType functionalInterfaceType) {
133     final boolean hasParams = expression.getParameterList().getParametersCount() > 0;
134     if (hasParams || getFunctionalInterfaceReturnType(functionalInterfaceType) != PsiType.VOID) {   //todo check that void lambdas without params check
135       
136       return !dependsOnTypeParams(functionalInterfaceType, functionalInterfaceType, expression);
137     }
138     return true;
139   }
140
141   @Nullable
142   static MethodSignature getFunction(PsiClass psiClass) {
143     if (psiClass == null) return null;
144     final List<HierarchicalMethodSignature> functions = findFunctionCandidates(psiClass);
145     if (functions != null && functions.size() == 1) {
146       return functions.get(0);
147     }
148     return null;
149   }
150
151
152   private static boolean overridesPublicObjectMethod(PsiMethod psiMethod) {
153     boolean overrideObject = false;
154     for (PsiMethod superMethod : psiMethod.findDeepestSuperMethods()) {
155       final PsiClass containingClass = superMethod.getContainingClass();
156       if (containingClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName())) {
157         if (superMethod.hasModifierProperty(PsiModifier.PUBLIC)) {
158           overrideObject = true;
159           break;
160         }
161       }
162     }
163     return overrideObject;
164   }
165
166   private static MethodSignature getMethodSignature(PsiMethod method, PsiClass psiClass, PsiClass containingClass) {
167     final MethodSignature methodSignature;
168     if (containingClass != null && containingClass != psiClass) {
169       methodSignature = method.getSignature(TypeConversionUtil.getSuperClassSubstitutor(containingClass, psiClass, PsiSubstitutor.EMPTY));
170     }
171     else {
172       methodSignature = method.getSignature(PsiSubstitutor.EMPTY);
173     }
174     return methodSignature;
175   }
176
177   @Nullable
178   private static List<HierarchicalMethodSignature> hasSubsignature(List<HierarchicalMethodSignature> signatures) {
179     for (HierarchicalMethodSignature signature : signatures) {
180       boolean subsignature = true;
181       for (HierarchicalMethodSignature methodSignature : signatures) {
182         if (!signature.equals(methodSignature)) {
183           if (!MethodSignatureUtil.isSubsignature(signature, methodSignature) &&
184               !skipMethod(signature, methodSignature)) {
185             subsignature = false;
186             break;
187           }
188         }
189       }
190       if (subsignature) return Collections.singletonList(signature);
191     }
192     return signatures;
193   }
194
195   private static boolean skipMethod(HierarchicalMethodSignature signature,
196                                     HierarchicalMethodSignature methodSignature) {
197     //not generic
198     if (methodSignature.getTypeParameters().length == 0) {
199       return false;
200     }
201     //foreign class
202     return signature.getMethod().getContainingClass() != methodSignature.getMethod().getContainingClass();
203   }
204
205   @Nullable
206   public static List<HierarchicalMethodSignature> findFunctionCandidates(PsiClass psiClass) {
207     if (psiClass != null && psiClass.isInterface() && !psiClass.isAnnotationType()) {
208       final List<HierarchicalMethodSignature> methods = new ArrayList<HierarchicalMethodSignature>();
209       final Collection<HierarchicalMethodSignature> visibleSignatures = psiClass.getVisibleSignatures();
210       for (HierarchicalMethodSignature signature : visibleSignatures) {
211         final PsiMethod psiMethod = signature.getMethod();
212         if (!psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) continue;
213         if (psiMethod.hasModifierProperty(PsiModifier.STATIC)) continue;
214         if (!overridesPublicObjectMethod(psiMethod)) {
215           methods.add(signature);
216         }
217       }
218
219       return hasSubsignature(methods);
220     }
221     return null;
222   }
223
224
225   @Nullable
226   private static PsiType getReturnType(PsiClass psiClass, MethodSignature methodSignature) {
227     final PsiMethod method = getMethod(psiClass, methodSignature);
228     if (method != null) {
229       final PsiClass containingClass = method.getContainingClass();
230       if (containingClass == null) return null;
231       return TypeConversionUtil.getSuperClassSubstitutor(containingClass, psiClass, PsiSubstitutor.EMPTY).substitute(method.getReturnType());
232     }
233     else {
234       return null;
235     }
236   }
237
238   @Nullable
239   private static PsiMethod getMethod(PsiClass psiClass, MethodSignature methodSignature) {
240     final PsiMethod[] methodsByName = psiClass.findMethodsByName(methodSignature.getName(), true);
241     for (PsiMethod psiMethod : methodsByName) {
242       if (MethodSignatureUtil
243         .areSignaturesEqual(getMethodSignature(psiMethod, psiClass, psiMethod.getContainingClass()), methodSignature)) {
244         return psiMethod;
245       }
246     }
247     return null;
248   }
249
250   public static int getLambdaIdx(PsiExpressionList expressionList, final PsiElement element) {
251     PsiExpression[] expressions = expressionList.getExpressions();
252     for (int i = 0; i < expressions.length; i++) {
253       PsiExpression expression = expressions[i];
254       if (PsiTreeUtil.isAncestor(expression, element, false)) {
255         return i;
256       }
257     }
258     return -1;
259   }
260
261   public static boolean dependsOnTypeParams(PsiType type,
262                                             PsiType functionalInterfaceType,
263                                             PsiElement lambdaExpression,
264                                             PsiTypeParameter... param2Check) {
265     return depends(type, new TypeParamsChecker(lambdaExpression,
266                                                PsiUtil.resolveClassInType(functionalInterfaceType)), param2Check);
267   }
268
269   public static boolean depends(PsiType type, TypeParamsChecker visitor, PsiTypeParameter... param2Check) {
270     if (!visitor.startedInference()) return false;
271     final Boolean accept = type.accept(visitor);
272     if (param2Check.length > 0) {
273       return visitor.used(param2Check);
274     }
275     return accept != null && accept.booleanValue();
276   }
277
278   @Nullable
279   public static PsiType getFunctionalInterfaceType(PsiElement expression, final boolean tryToSubstitute) {
280     PsiElement parent = expression.getParent();
281     PsiElement element = expression;
282     while (parent instanceof PsiParenthesizedExpression || parent instanceof PsiConditionalExpression) {
283       if (parent instanceof PsiConditionalExpression &&
284           ((PsiConditionalExpression)parent).getThenExpression() != element &&
285           ((PsiConditionalExpression)parent).getElseExpression() != element) break;
286       element = parent;
287       parent = parent.getParent();
288     }
289     if (parent instanceof PsiArrayInitializerExpression) {
290       final PsiType psiType = ((PsiArrayInitializerExpression)parent).getType();
291       if (psiType instanceof PsiArrayType) {
292         return ((PsiArrayType)psiType).getComponentType();
293       }
294     } else if (parent instanceof PsiTypeCastExpression) {
295       final PsiType castType = ((PsiTypeCastExpression)parent).getType();
296       if (castType instanceof PsiIntersectionType) {
297         final PsiType conjunct = extractFunctionalConjunct((PsiIntersectionType)castType);
298         if (conjunct != null) return conjunct;
299       }
300       return castType;
301     }
302     else if (parent instanceof PsiVariable) {
303       return ((PsiVariable)parent).getType();
304     }
305     else if (parent instanceof PsiAssignmentExpression && expression instanceof PsiExpression && !PsiUtil.isOnAssignmentLeftHand((PsiExpression)expression)) {
306       final PsiExpression lExpression = ((PsiAssignmentExpression)parent).getLExpression();
307       return lExpression.getType();
308     }
309     else if (parent instanceof PsiExpressionList) {
310       final PsiExpressionList expressionList = (PsiExpressionList)parent;
311       final int lambdaIdx = getLambdaIdx(expressionList, expression);
312       if (lambdaIdx > -1) {
313
314         PsiElement gParent = expressionList.getParent();
315
316         if (gParent instanceof PsiAnonymousClass) {
317           gParent = gParent.getParent();
318         }
319
320         if (gParent instanceof PsiCall) {
321           final PsiCall contextCall = (PsiCall)gParent;
322           final MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(contextCall.getArgumentList());
323           if (properties != null && properties.isApplicabilityCheck()) { //todo simplification
324             final PsiParameter[] parameters = properties.getMethod().getParameterList().getParameters();
325             final int finalLambdaIdx = adjustLambdaIdx(lambdaIdx, properties.getMethod(), parameters);
326             if (finalLambdaIdx < parameters.length) {
327               return properties.getSubstitutor().substitute(getNormalizedType(parameters[finalLambdaIdx]));
328             }
329           }
330           final Map<PsiElement, PsiType> map = ourFunctionTypes.get();
331           if (map != null) {
332             final PsiType type = map.get(expression);
333             if (type != null) {
334               return type;
335             }
336           }
337           final JavaResolveResult resolveResult = contextCall.resolveMethodGenerics();
338             final PsiElement resolve = resolveResult.getElement();
339             if (resolve instanceof PsiMethod) {
340               final PsiParameter[] parameters = ((PsiMethod)resolve).getParameterList().getParameters();
341               final int finalLambdaIdx = adjustLambdaIdx(lambdaIdx, (PsiMethod)resolve, parameters);
342               if (finalLambdaIdx < parameters.length) {
343                 if (!tryToSubstitute) return getNormalizedType(parameters[finalLambdaIdx]);
344                 return PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, true, new Computable<PsiType>() {
345                   @Override
346                   public PsiType compute() {
347                     return resolveResult.getSubstitutor().substitute(getNormalizedType(parameters[finalLambdaIdx]));
348                   }
349                 });
350               }
351             }
352             return null;
353         }
354       }
355     }
356     else if (parent instanceof PsiReturnStatement) {
357       final PsiElement gParent = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class, PsiMethod.class);
358       if (gParent instanceof PsiLambdaExpression) {
359         return getFunctionalInterfaceTypeByContainingLambda((PsiLambdaExpression)gParent);
360       } else if (gParent instanceof PsiMethod) {
361         return ((PsiMethod)gParent).getReturnType();
362       }
363     }
364     else if (parent instanceof PsiLambdaExpression) {
365       return getFunctionalInterfaceTypeByContainingLambda((PsiLambdaExpression)parent);
366     }
367     return null;
368   }
369
370   @Nullable
371   private static PsiType extractFunctionalConjunct(PsiIntersectionType type) {
372     PsiType conjunct = null;
373     for (PsiType conjunctType : type.getConjuncts()) {
374       final PsiMethod interfaceMethod = getFunctionalInterfaceMethod(conjunctType);
375       if (interfaceMethod != null) {
376         if (conjunct != null && !conjunct.equals(conjunctType)) return null;
377         conjunct = conjunctType;
378       }
379     }
380     return conjunct;
381   }
382   
383   private static PsiType getFunctionalInterfaceTypeByContainingLambda(@NotNull PsiLambdaExpression parentLambda) {
384     final PsiType parentInterfaceType = parentLambda.getFunctionalInterfaceType();
385     return parentInterfaceType != null ? getFunctionalInterfaceReturnType(parentInterfaceType) : null;
386   }
387
388   private static int adjustLambdaIdx(int lambdaIdx, PsiMethod resolve, PsiParameter[] parameters) {
389     final int finalLambdaIdx;
390     if (resolve.isVarArgs() && lambdaIdx >= parameters.length) {
391       finalLambdaIdx = parameters.length - 1;
392     } else {
393       finalLambdaIdx = lambdaIdx;
394     }
395     return finalLambdaIdx;
396   }
397
398   private static PsiType getNormalizedType(PsiParameter parameter) {
399     final PsiType type = parameter.getType();
400     if (type instanceof PsiEllipsisType) {
401       return ((PsiEllipsisType)type).getComponentType();
402     }
403     return type;
404   }
405
406   public static boolean notInferredType(PsiType typeByExpression) {
407     return typeByExpression instanceof PsiMethodReferenceType || typeByExpression instanceof PsiLambdaExpressionType || typeByExpression instanceof PsiLambdaParameterType;
408   }
409
410   public static PsiReturnStatement[] getReturnStatements(PsiLambdaExpression lambdaExpression) {
411     final PsiElement body = lambdaExpression.getBody();
412     return body instanceof PsiCodeBlock ? PsiUtil.findReturnStatements((PsiCodeBlock)body) : PsiReturnStatement.EMPTY_ARRAY;
413   }
414
415   public static List<PsiExpression> getReturnExpressions(PsiLambdaExpression lambdaExpression) {
416     final PsiElement body = lambdaExpression.getBody();
417     if (body instanceof PsiExpression) {
418       //if (((PsiExpression)body).getType() != PsiType.VOID) return Collections.emptyList();
419       return Collections.singletonList((PsiExpression)body);
420     }
421     final List<PsiExpression> result = new ArrayList<PsiExpression>();
422     for (PsiReturnStatement returnStatement : getReturnStatements(lambdaExpression)) {
423       final PsiExpression returnValue = returnStatement.getReturnValue();
424       if (returnValue != null) {
425         result.add(returnValue);
426       }
427     }
428     return result;
429   }
430
431   @Nullable
432   public static String checkFunctionalInterface(@NotNull PsiAnnotation annotation, @NotNull LanguageLevel languageLevel) {
433     if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && Comparing.strEqual(annotation.getQualifiedName(), CommonClassNames.JAVA_LANG_FUNCTIONAL_INTERFACE)) {
434       final PsiAnnotationOwner owner = annotation.getOwner();
435       if (owner instanceof PsiModifierList) {
436         final PsiElement parent = ((PsiModifierList)owner).getParent();
437         if (parent instanceof PsiClass) {
438           return LambdaHighlightingUtil.checkInterfaceFunctional((PsiClass)parent, ((PsiClass)parent).getName() + " is not a functional interface");
439         }
440       }
441     }
442     return null;
443   }
444
445   public static boolean isValidQualifier4InterfaceStaticMethodCall(@NotNull PsiMethod method,
446                                                                    @NotNull PsiReferenceExpression methodReferenceExpression,
447                                                                    @Nullable PsiElement scope, @NotNull LanguageLevel languageLevel) {
448     if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
449       final PsiExpression qualifierExpression = methodReferenceExpression.getQualifierExpression();
450       final PsiClass containingClass = method.getContainingClass();
451       if (containingClass != null && containingClass.isInterface() && method.hasModifierProperty(PsiModifier.STATIC)) {
452         return qualifierExpression == null && (scope instanceof PsiImportStaticStatement || PsiTreeUtil.isAncestor(containingClass, methodReferenceExpression, true))||
453                qualifierExpression instanceof PsiReferenceExpression && ((PsiReferenceExpression)qualifierExpression).resolve() == containingClass;
454       }
455     }
456     return true;
457   }
458
459   //JLS 14.8 Expression Statements
460   public static boolean isExpressionStatementExpression(PsiElement body) {
461     return body instanceof PsiAssignmentExpression ||
462            body instanceof PsiPrefixExpression &&
463            (((PsiPrefixExpression)body).getOperationTokenType() == JavaTokenType.PLUSPLUS ||
464             ((PsiPrefixExpression)body).getOperationTokenType() == JavaTokenType.MINUSMINUS) ||
465            body instanceof PsiPostfixExpression ||
466            body instanceof PsiCallExpression ||
467            body instanceof PsiReferenceExpression && !body.isPhysical();
468   }
469
470   public static PsiExpression extractSingleExpressionFromBody(PsiElement body) {
471     PsiExpression expression = null;
472     if (body instanceof PsiExpression) {
473       expression = (PsiExpression)body;
474     }
475     else if (body instanceof PsiCodeBlock) {
476       final PsiStatement[] statements = ((PsiCodeBlock)body).getStatements();
477       if (statements.length == 1) {
478         if (statements[0] instanceof PsiReturnStatement) {
479           expression = ((PsiReturnStatement)statements[0]).getReturnValue();
480         }
481         else if (statements[0] instanceof PsiExpressionStatement) {
482           expression = ((PsiExpressionStatement)statements[0]).getExpression();
483         } else if (statements[0] instanceof PsiBlockStatement) {
484           return extractSingleExpressionFromBody(((PsiBlockStatement)statements[0]).getCodeBlock());
485         }
486       }
487     }
488     else if (body instanceof PsiBlockStatement) {
489       return extractSingleExpressionFromBody(((PsiBlockStatement)body).getCodeBlock());
490     }
491     else if (body instanceof PsiExpressionStatement) {
492       expression = ((PsiExpressionStatement)body).getExpression();
493     }
494     return expression;
495   }
496
497   // http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.1
498   // A lambda expression or a method reference expression is potentially compatible with a type variable 
499   // if the type variable is a type parameter of the candidate method.
500   public static boolean isPotentiallyCompatibleWithTypeParameter(PsiFunctionalExpression expression,
501                                                                  PsiExpressionList argsList,
502                                                                  PsiMethod method) {
503     if (!Registry.is("JDK8042508.bug.fixed", false)) {
504       final PsiCallExpression callExpression = PsiTreeUtil.getParentOfType(argsList, PsiCallExpression.class);
505       if (callExpression == null || callExpression.getTypeArguments().length > 0) {
506         return false;
507       }
508     }
509
510     final int lambdaIdx = getLambdaIdx(argsList, expression);
511     if (lambdaIdx >= 0) {
512       final PsiParameter[] parameters = method.getParameterList().getParameters();
513       final PsiParameter lambdaParameter = parameters[Math.min(lambdaIdx, parameters.length - 1)];
514       final PsiClass paramClass = PsiUtil.resolveClassInType(lambdaParameter.getType());
515       if (paramClass instanceof PsiTypeParameter && ((PsiTypeParameter)paramClass).getOwner() == method) {
516         return true;
517       }
518     }
519     return false;
520   }
521   
522   public static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
523     private PsiMethod myMethod;
524     private final PsiClass myClass;
525     public final Set<PsiTypeParameter> myUsedTypeParams = new HashSet<PsiTypeParameter>();
526
527     public TypeParamsChecker(PsiElement expression, PsiClass aClass) {
528       myClass = aClass;
529       PsiElement parent = expression != null ? expression.getParent() : null;
530       while (parent instanceof PsiParenthesizedExpression) {
531         parent = parent.getParent();
532       }
533       if (parent instanceof PsiExpressionList) {
534         final PsiElement gParent = parent.getParent();
535         if (gParent instanceof PsiCall) {
536           final MethodCandidateInfo.CurrentCandidateProperties pair = MethodCandidateInfo.getCurrentMethod(parent);
537           myMethod = pair != null ? pair.getMethod() : null;
538           if (myMethod == null) {
539             myMethod = ((PsiCall)gParent).resolveMethod();
540           }
541           if (myMethod != null && PsiTreeUtil.isAncestor(myMethod, expression, false)) {
542             myMethod = null;
543           }
544         }
545       }
546     }
547
548     public boolean startedInference() {
549       return myMethod != null;
550     }
551
552     @Override
553     public Boolean visitClassType(PsiClassType classType) {
554       boolean used = false;
555       for (PsiType paramType : classType.getParameters()) {
556         final Boolean paramAccepted = paramType.accept(this);
557         used |= paramAccepted != null && paramAccepted.booleanValue();
558       }
559       final PsiClass resolve = classType.resolve();
560       if (resolve instanceof PsiTypeParameter) {
561         final PsiTypeParameter typeParameter = (PsiTypeParameter)resolve;
562         if (check(typeParameter)) {
563           myUsedTypeParams.add(typeParameter);
564           return true;
565         }
566       }
567       return used;
568     }
569
570     @Nullable
571     @Override
572     public Boolean visitWildcardType(PsiWildcardType wildcardType) {
573       final PsiType bound = wildcardType.getBound();
574       if (bound != null) return bound.accept(this);
575       return false;
576     }
577
578     @Nullable
579     @Override
580     public Boolean visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
581       return visitWildcardType(capturedWildcardType.getWildcard());
582     }
583
584     @Nullable
585     @Override
586     public Boolean visitLambdaExpressionType(PsiLambdaExpressionType lambdaExpressionType) {
587       return true;
588     }
589
590     @Nullable
591     @Override
592     public Boolean visitArrayType(PsiArrayType arrayType) {
593       return arrayType.getComponentType().accept(this);
594     }
595
596     @Override
597     public Boolean visitType(PsiType type) {
598       return false;
599     }
600
601     private boolean check(PsiTypeParameter check) {
602       final PsiTypeParameterListOwner owner = check.getOwner();
603       if (owner == myMethod) {
604         return true;
605       }
606       else if (owner == myClass) {
607         return true;
608       }
609       return false;
610     }
611
612     public boolean used(PsiTypeParameter... parameters) {
613       for (PsiTypeParameter parameter : parameters) {
614         if (myUsedTypeParams.contains(parameter)) return true;
615       }
616       return false;
617     }
618   }
619 }