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