java: prohibit caching when using thread-local types imposed on expressions and decla...
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / PsiImplUtil.java
1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.psi.impl;
3
4 import com.intellij.codeInsight.AnnotationTargetUtil;
5 import com.intellij.codeInsight.AnnotationUtil;
6 import com.intellij.lang.ASTNode;
7 import com.intellij.lang.FileASTNode;
8 import com.intellij.openapi.application.ApplicationManager;
9 import com.intellij.openapi.diagnostic.Logger;
10 import com.intellij.openapi.project.Project;
11 import com.intellij.openapi.util.Comparing;
12 import com.intellij.openapi.util.Key;
13 import com.intellij.openapi.util.text.StringUtil;
14 import com.intellij.openapi.vfs.VirtualFile;
15 import com.intellij.psi.*;
16 import com.intellij.psi.filters.ElementFilter;
17 import com.intellij.psi.impl.light.LightClassReference;
18 import com.intellij.psi.impl.light.LightJavaModule;
19 import com.intellij.psi.impl.source.DummyHolder;
20 import com.intellij.psi.impl.source.PsiClassReferenceType;
21 import com.intellij.psi.impl.source.PsiImmediateClassType;
22 import com.intellij.psi.impl.source.resolve.ResolveCache;
23 import com.intellij.psi.impl.source.tree.*;
24 import com.intellij.psi.javadoc.PsiDocComment;
25 import com.intellij.psi.scope.ElementClassHint;
26 import com.intellij.psi.scope.PsiScopeProcessor;
27 import com.intellij.psi.scope.processor.FilterScopeProcessor;
28 import com.intellij.psi.scope.util.PsiScopesUtil;
29 import com.intellij.psi.search.GlobalSearchScope;
30 import com.intellij.psi.search.LocalSearchScope;
31 import com.intellij.psi.search.PackageScope;
32 import com.intellij.psi.search.SearchScope;
33 import com.intellij.psi.tree.IElementType;
34 import com.intellij.psi.tree.TokenSet;
35 import com.intellij.psi.util.PsiTreeUtil;
36 import com.intellij.psi.util.PsiUtil;
37 import com.intellij.psi.util.PsiUtilCore;
38 import com.intellij.util.IncorrectOperationException;
39 import com.intellij.util.PairFunction;
40 import com.intellij.util.SmartList;
41 import com.intellij.util.containers.ContainerUtil;
42 import org.jetbrains.annotations.Contract;
43 import org.jetbrains.annotations.NonNls;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
46
47 import java.util.Arrays;
48 import java.util.List;
49
50 public class PsiImplUtil {
51   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiImplUtil");
52
53   private PsiImplUtil() { }
54
55   @NotNull
56   public static PsiMethod[] getConstructors(@NotNull PsiClass aClass) {
57     List<PsiMethod> result = null;
58     for (PsiMethod method : aClass.getMethods()) {
59       if (method.isConstructor() && method.getName().equals(aClass.getName())) {
60         if (result == null) result = ContainerUtil.newSmartList();
61         result.add(method);
62       }
63     }
64     return result == null ? PsiMethod.EMPTY_ARRAY : result.toArray(PsiMethod.EMPTY_ARRAY);
65   }
66
67   @Nullable
68   public static PsiAnnotationMemberValue findDeclaredAttributeValue(@NotNull PsiAnnotation annotation, @NonNls @Nullable String attributeName) {
69     PsiNameValuePair attribute = AnnotationUtil.findDeclaredAttribute(annotation, attributeName);
70     return attribute == null ? null : attribute.getValue();
71   }
72
73   @Nullable
74   public static PsiAnnotationMemberValue findAttributeValue(@NotNull PsiAnnotation annotation, @Nullable @NonNls String attributeName) {
75     final PsiAnnotationMemberValue value = findDeclaredAttributeValue(annotation, attributeName);
76     if (value != null) return value;
77
78     if (attributeName == null) attributeName = "value";
79     final PsiJavaCodeReferenceElement referenceElement = annotation.getNameReferenceElement();
80     if (referenceElement != null) {
81       PsiElement resolved = referenceElement.resolve();
82       if (resolved != null) {
83         PsiMethod[] methods = ((PsiClass)resolved).findMethodsByName(attributeName, false);
84         for (PsiMethod method : methods) {
85           if (PsiUtil.isAnnotationMethod(method)) {
86             return ((PsiAnnotationMethod)method).getDefaultValue();
87           }
88         }
89       }
90     }
91     return null;
92   }
93
94   @NotNull
95   public static PsiTypeParameter[] getTypeParameters(@NotNull PsiTypeParameterListOwner owner) {
96     final PsiTypeParameterList typeParameterList = owner.getTypeParameterList();
97     if (typeParameterList != null) {
98       return typeParameterList.getTypeParameters();
99     }
100     return PsiTypeParameter.EMPTY_ARRAY;
101   }
102
103   @NotNull
104   public static PsiJavaCodeReferenceElement[] namesToPackageReferences(@NotNull PsiManager manager, @NotNull String[] names) {
105     PsiJavaCodeReferenceElement[] refs = new PsiJavaCodeReferenceElement[names.length];
106     for (int i = 0; i < names.length; i++) {
107       String name = names[i];
108       try {
109         refs[i] = JavaPsiFacade.getElementFactory(manager.getProject()).createPackageReferenceElement(name);
110       }
111       catch (IncorrectOperationException e) {
112         LOG.error(e);
113       }
114     }
115     return refs;
116   }
117
118   public static int getParameterIndex(@NotNull PsiParameter parameter, @NotNull PsiParameterList parameterList) {
119     PsiElement parameterParent = parameter.getParent();
120     assert parameterParent == parameterList : parameterList +"; "+parameterParent;
121     PsiParameter[] parameters = parameterList.getParameters();
122     for (int i = 0; i < parameters.length; i++) {
123       PsiParameter paramInList = parameters[i];
124       if (parameter.equals(paramInList)) return i;
125     }
126     String name = parameter.getName();
127     PsiParameter suspect = null;
128     int i;
129     for (i = parameters.length - 1; i >= 0; i--) {
130       PsiParameter paramInList = parameters[i];
131       if (Comparing.equal(name, paramInList.getName())) {
132         suspect = paramInList;
133         break;
134       }
135     }
136     String message = parameter + ":" + parameter.getClass() + " not found among parameters: " + Arrays.asList(parameters) + "." +
137                      " parameterList' parent: " + parameterList.getParent() + ";" +
138                      " parameter.isValid()=" + parameter.isValid() + ";" +
139                      " parameterList.isValid()= " + parameterList.isValid() + ";" +
140                      " parameterList stub: " + (parameterList instanceof StubBasedPsiElement ? ((StubBasedPsiElement)parameterList).getStub() : "---") + "; " +
141                      " parameter stub: "+(parameter instanceof StubBasedPsiElement ? ((StubBasedPsiElement)parameter).getStub() : "---") + ";" +
142                      " suspect: " + suspect +" (index="+i+"); " + (suspect==null?null:suspect.getClass()) +
143                      " suspect stub: "+(suspect instanceof StubBasedPsiElement ? ((StubBasedPsiElement)suspect).getStub() : suspect == null ? "-null-" : "---"+suspect.getClass()) + ";" +
144                      " parameter.equals(suspect) = " + parameter.equals(suspect) + "; " +
145                      " parameter.getNode() == suspect.getNode():  " + (parameter.getNode() == (suspect==null ? null : suspect.getNode())) + "; " +
146                      "."
147       ;
148     LOG.error(message);
149     return i;
150   }
151
152   public static int getTypeParameterIndex(@NotNull PsiTypeParameter typeParameter, @NotNull PsiTypeParameterList typeParameterList) {
153     PsiTypeParameter[] typeParameters = typeParameterList.getTypeParameters();
154     for (int i = 0; i < typeParameters.length; i++) {
155       if (typeParameter.equals(typeParameters[i])) return i;
156     }
157     LOG.error(typeParameter + " in " + typeParameterList);
158     return -1;
159   }
160
161   @NotNull
162   public static Object[] getReferenceVariantsByFilter(@NotNull PsiJavaCodeReferenceElement reference, @NotNull ElementFilter filter) {
163     FilterScopeProcessor processor = new FilterScopeProcessor(filter);
164     PsiScopesUtil.resolveAndWalk(processor, reference, null, true);
165     return processor.getResults().toArray();
166   }
167
168   public static boolean processDeclarationsInMethod(@NotNull final PsiMethod method,
169                                                     @NotNull final PsiScopeProcessor processor,
170                                                     @NotNull final ResolveState state,
171                                                     PsiElement lastParent,
172                                                     @NotNull final PsiElement place) {
173     if (lastParent instanceof DummyHolder) lastParent = lastParent.getFirstChild();
174     boolean fromBody = lastParent instanceof PsiCodeBlock;
175     PsiTypeParameterList typeParameterList = method.getTypeParameterList();
176     return processDeclarationsInMethodLike(method, processor, state, place, fromBody, typeParameterList);
177   }
178
179   public static boolean processDeclarationsInLambda(@NotNull final PsiLambdaExpression lambda,
180                                                     @NotNull final PsiScopeProcessor processor,
181                                                     @NotNull final ResolveState state,
182                                                     final PsiElement lastParent,
183                                                     @NotNull final PsiElement place) {
184     final boolean fromBody = lastParent != null && lastParent == lambda.getBody();
185     return processDeclarationsInMethodLike(lambda, processor, state, place, fromBody, null);
186   }
187
188   private static boolean processDeclarationsInMethodLike(@NotNull final PsiParameterListOwner element,
189                                                          @NotNull final PsiScopeProcessor processor,
190                                                          @NotNull final ResolveState state,
191                                                          @NotNull final PsiElement place,
192                                                          final boolean fromBody,
193                                                          @Nullable final PsiTypeParameterList typeParameterList) {
194     processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, element);
195
196     if (typeParameterList != null) {
197       final ElementClassHint hint = processor.getHint(ElementClassHint.KEY);
198       if (hint == null || hint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) {
199         if (!typeParameterList.processDeclarations(processor, state, null, place)) return false;
200       }
201     }
202
203     if (fromBody) {
204       final PsiParameter[] parameters = element.getParameterList().getParameters();
205       for (PsiParameter parameter : parameters) {
206         if (!processor.execute(parameter, state)) return false;
207       }
208     }
209
210     return true;
211   }
212
213   public static boolean processDeclarationsInResourceList(@NotNull final PsiResourceList resourceList,
214                                                           @NotNull final PsiScopeProcessor processor,
215                                                           @NotNull final ResolveState state,
216                                                           final PsiElement lastParent) {
217     final ElementClassHint hint = processor.getHint(ElementClassHint.KEY);
218     if (hint != null && !hint.shouldProcess(ElementClassHint.DeclarationKind.VARIABLE)) return true;
219
220     for (PsiResourceListElement resource : resourceList) {
221       if (resource == lastParent) break;
222       if (resource instanceof PsiResourceVariable && !processor.execute(resource, state)) return false;
223     }
224
225     return true;
226   }
227
228   public static boolean hasTypeParameters(@NotNull PsiTypeParameterListOwner owner) {
229     final PsiTypeParameterList typeParameterList = owner.getTypeParameterList();
230     return typeParameterList != null && typeParameterList.getTypeParameters().length != 0;
231   }
232
233   @NotNull
234   public static PsiType[] typesByReferenceParameterList(@NotNull PsiReferenceParameterList parameterList) {
235     PsiTypeElement[] typeElements = parameterList.getTypeParameterElements();
236
237     return typesByTypeElements(typeElements);
238   }
239
240   @NotNull
241   public static PsiType[] typesByTypeElements(@NotNull PsiTypeElement[] typeElements) {
242     PsiType[] types = PsiType.createArray(typeElements.length);
243     for (int i = 0; i < types.length; i++) {
244       types[i] = typeElements[i].getType();
245     }
246     if (types.length == 1 && types[0] instanceof PsiDiamondType) {
247       return ((PsiDiamondType)types[0]).resolveInferredTypes().getTypes();
248     }
249     return types;
250   }
251
252   @NotNull
253   public static PsiType getType(@NotNull PsiClassObjectAccessExpression classAccessExpression) {
254     GlobalSearchScope resolveScope = classAccessExpression.getResolveScope();
255     PsiManager manager = classAccessExpression.getManager();
256     final PsiClass classClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Class", resolveScope);
257     if (classClass == null) {
258       return new PsiClassReferenceType(new LightClassReference(manager, "Class", "java.lang.Class", resolveScope), null);
259     }
260     if (!PsiUtil.isLanguageLevel5OrHigher(classAccessExpression)) {
261       //Raw java.lang.Class
262       return JavaPsiFacade.getElementFactory(manager.getProject()).createType(classClass);
263     }
264
265     PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
266     PsiType operandType = classAccessExpression.getOperand().getType();
267     if (operandType instanceof PsiPrimitiveType && !PsiType.NULL.equals(operandType)) {
268       if (PsiType.VOID.equals(operandType)) {
269         operandType = JavaPsiFacade.getElementFactory(manager.getProject())
270             .createTypeByFQClassName("java.lang.Void", classAccessExpression.getResolveScope());
271       }
272       else {
273         operandType = ((PsiPrimitiveType)operandType).getBoxedType(classAccessExpression);
274       }
275     }
276     final PsiTypeParameter[] typeParameters = classClass.getTypeParameters();
277     if (typeParameters.length == 1) {
278       substitutor = substitutor.put(typeParameters[0], operandType);
279     }
280
281     return new PsiImmediateClassType(classClass, substitutor);
282   }
283
284   @Nullable
285   public static PsiAnnotation findAnnotation(@Nullable PsiAnnotationOwner annotationOwner, @NotNull String qualifiedName) {
286     if (annotationOwner == null) return null;
287
288     PsiAnnotation[] annotations = annotationOwner.getAnnotations();
289     if (annotations.length == 0) return null;
290
291     String shortName = StringUtil.getShortName(qualifiedName);
292     for (PsiAnnotation annotation : annotations) {
293       PsiJavaCodeReferenceElement referenceElement = annotation.getNameReferenceElement();
294       if (referenceElement != null && shortName.equals(referenceElement.getReferenceName())) {
295         if (qualifiedName.equals(annotation.getQualifiedName())) {
296           return annotation;
297         }
298       }
299     }
300
301     return null;
302   }
303
304   /** @deprecated use {@link AnnotationTargetUtil#findAnnotationTarget(PsiAnnotation, PsiAnnotation.TargetType...)} (to be removed ion IDEA 17) */
305   @Deprecated
306   public static PsiAnnotation.TargetType findApplicableTarget(@NotNull PsiAnnotation annotation, @NotNull PsiAnnotation.TargetType... types) {
307     return AnnotationTargetUtil.findAnnotationTarget(annotation, types);
308   }
309
310   /** @deprecated use {@link AnnotationTargetUtil#findAnnotationTarget(PsiClass, PsiAnnotation.TargetType...)} (to be removed ion IDEA 17) */
311   @Deprecated
312   public static PsiAnnotation.TargetType findApplicableTarget(@NotNull PsiClass annotationType, @NotNull PsiAnnotation.TargetType... types) {
313     return AnnotationTargetUtil.findAnnotationTarget(annotationType, types);
314   }
315
316   /** @deprecated use {@link AnnotationTargetUtil#getTargetsForLocation(PsiAnnotationOwner)} (to be removed ion IDEA 17) */
317   @Deprecated
318   @NotNull
319   public static PsiAnnotation.TargetType[] getTargetsForLocation(@Nullable PsiAnnotationOwner owner) {
320     return AnnotationTargetUtil.getTargetsForLocation(owner);
321   }
322
323   @Nullable
324   public static ASTNode findDocComment(@NotNull CompositeElement element) {
325     TreeElement node = element.getFirstChildNode();
326     while (node != null && isWhitespaceOrComment(node) && !(node.getPsi() instanceof PsiDocComment)) {
327       node = node.getTreeNext();
328     }
329
330     return node == null || node.getElementType() != JavaDocElementType.DOC_COMMENT ? null : node;
331   }
332
333   /**
334    * Types should be proceed by the callers themselves
335    */
336   @Deprecated
337   public static PsiType normalizeWildcardTypeByPosition(@NotNull PsiType type, @NotNull PsiExpression expression) {
338     PsiUtilCore.ensureValid(expression);
339     PsiUtil.ensureValidType(type);
340
341     PsiExpression topLevel = expression;
342     while (topLevel.getParent() instanceof PsiArrayAccessExpression &&
343            ((PsiArrayAccessExpression)topLevel.getParent()).getArrayExpression() == topLevel) {
344       topLevel = (PsiExpression)topLevel.getParent();
345     }
346
347     if (topLevel instanceof PsiArrayAccessExpression && !PsiUtil.isAccessedForWriting(topLevel)) {
348       return PsiUtil.captureToplevelWildcards(type, expression);
349     }
350
351     final PsiType normalized = doNormalizeWildcardByPosition(type, expression, topLevel);
352     LOG.assertTrue(normalized.isValid(), type);
353     if (normalized instanceof PsiClassType && !PsiUtil.isAccessedForWriting(topLevel)) {
354       return PsiUtil.captureToplevelWildcards(normalized, expression);
355     }
356
357     return normalized;
358   }
359
360   private static PsiType doNormalizeWildcardByPosition(PsiType type, @NotNull PsiExpression expression, @NotNull PsiExpression topLevel) {
361     if (type instanceof PsiWildcardType) {
362       final PsiWildcardType wildcardType = (PsiWildcardType)type;
363
364       if (PsiUtil.isAccessedForWriting(topLevel)) {
365         return wildcardType.isSuper() ? wildcardType.getBound() : PsiCapturedWildcardType.create(wildcardType, expression);
366       }
367       else {
368         if (wildcardType.isExtends()) {
369           return wildcardType.getBound();
370         }
371         return PsiType.getJavaLangObject(expression.getManager(), expression.getResolveScope());
372       }
373     }
374     if (type instanceof PsiArrayType) {
375       final PsiType componentType = ((PsiArrayType)type).getComponentType();
376       final PsiType normalizedComponentType = doNormalizeWildcardByPosition(componentType, expression, topLevel);
377       if (normalizedComponentType != componentType) {
378         return normalizedComponentType.createArrayType();
379       }
380     }
381
382     return type;
383   }
384
385   @NotNull
386   public static SearchScope getMemberUseScope(@NotNull PsiMember member) {
387     PsiFile file = member.getContainingFile();
388     PsiElement topElement = file == null ? member : file;
389     Project project = topElement.getProject();
390     final GlobalSearchScope maximalUseScope = ResolveScopeManager.getInstance(project).getUseScope(topElement);
391     if (isInServerPage(file)) return maximalUseScope;
392
393     PsiClass aClass = member.getContainingClass();
394     if (aClass instanceof PsiAnonymousClass && !(aClass instanceof PsiEnumConstantInitializer &&
395                                                  member instanceof PsiMethod &&
396                                                  member.hasModifierProperty(PsiModifier.PUBLIC) &&
397                                                  ((PsiMethod)member).findSuperMethods().length > 0)) {
398       //member from anonymous class can be called from outside the class
399       PsiElement methodCallExpr = PsiUtil.isLanguageLevel8OrHigher(aClass) ? PsiTreeUtil.getTopmostParentOfType(aClass, PsiStatement.class)
400                                                                            : PsiTreeUtil.getParentOfType(aClass, PsiMethodCallExpression.class);
401       return new LocalSearchScope(methodCallExpr != null ? methodCallExpr : aClass);
402     }
403
404     PsiModifierList modifierList = member.getModifierList();
405     int accessLevel = modifierList == null ? PsiUtil.ACCESS_LEVEL_PUBLIC : PsiUtil.getAccessLevel(modifierList);
406     if (accessLevel == PsiUtil.ACCESS_LEVEL_PUBLIC || accessLevel == PsiUtil.ACCESS_LEVEL_PROTECTED) {
407       if (member instanceof PsiMethod && ((PsiMethod)member).isConstructor()) {
408         PsiClass containingClass = member.getContainingClass();
409         if (containingClass != null) {
410           //constructors cannot be overridden so their use scope can't be wider than their class's
411           return containingClass.getUseScope();
412         }
413       }
414       return maximalUseScope; // class use scope doesn't matter, since another very visible class can inherit from aClass
415     }
416     if (accessLevel == PsiUtil.ACCESS_LEVEL_PRIVATE) {
417       PsiClass topClass = PsiUtil.getTopLevelClass(member);
418       return topClass != null ? new LocalSearchScope(topClass) : file == null ? maximalUseScope : new LocalSearchScope(file);
419     }
420     if (file instanceof PsiJavaFile) {
421       PsiPackage aPackage = JavaPsiFacade.getInstance(project).findPackage(((PsiJavaFile)file).getPackageName());
422       if (aPackage != null) {
423         SearchScope scope = PackageScope.packageScope(aPackage, false);
424         return scope.intersectWith(maximalUseScope);
425       }
426     }
427     return maximalUseScope;
428   }
429
430   public static boolean isInServerPage(@Nullable final PsiElement element) {
431     return getServerPageFile(element) != null;
432   }
433
434   @Nullable
435   private static ServerPageFile getServerPageFile(final PsiElement element) {
436     final PsiFile psiFile = PsiUtilCore.getTemplateLanguageFile(element);
437     return psiFile instanceof ServerPageFile ? (ServerPageFile)psiFile : null;
438   }
439
440   public static PsiElement setName(@NotNull PsiElement element, @NotNull String name) throws IncorrectOperationException {
441     PsiManager manager = element.getManager();
442     PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject());
443     PsiIdentifier newNameIdentifier = factory.createIdentifier(name);
444     return element.replace(newNameIdentifier);
445   }
446
447   public static boolean isDeprecatedByAnnotation(@NotNull PsiModifierListOwner owner) {
448     return AnnotationUtil.findAnnotation(owner, CommonClassNames.JAVA_LANG_DEPRECATED) != null;
449   }
450
451   public static boolean isDeprecatedByDocTag(@NotNull PsiJavaDocumentedElement owner) {
452     PsiDocComment docComment = owner.getDocComment();
453     return docComment != null && docComment.findTagByName("deprecated") != null;
454   }
455
456   /**
457    * Checks if the given PSI element is deprecated with annotation or JavaDoc tag.
458    * <br>
459    * It is suitable for elements other than {@link PsiDocCommentOwner}.
460    */
461   public static boolean isDeprecated(@NotNull PsiElement psiElement) {
462     if (psiElement instanceof PsiDocCommentOwner) {
463       return ((PsiDocCommentOwner)psiElement).isDeprecated();
464     }
465     if (psiElement instanceof PsiModifierListOwner && isDeprecatedByAnnotation((PsiModifierListOwner) psiElement)) {
466       return true;
467     }
468     if (psiElement instanceof PsiJavaDocumentedElement) {
469       return isDeprecatedByDocTag((PsiJavaDocumentedElement)psiElement);
470     }
471     return false;
472   }
473
474
475   @Nullable
476   public static PsiJavaDocumentedElement findDocCommentOwner(@NotNull PsiDocComment comment) {
477     PsiElement parent = comment.getParent();
478     if (parent instanceof PsiJavaDocumentedElement) {
479       PsiJavaDocumentedElement owner = (PsiJavaDocumentedElement)parent;
480       if (owner.getDocComment() == comment) {
481         return owner;
482       }
483     }
484     return null;
485   }
486
487   @Nullable
488   public static PsiAnnotationMemberValue setDeclaredAttributeValue(@NotNull PsiAnnotation psiAnnotation,
489                                                                    @Nullable String attributeName,
490                                                                    @Nullable PsiAnnotationMemberValue value,
491                                                                    @NotNull PairFunction<? super Project, ? super String, ? extends PsiAnnotation> annotationCreator) {
492     PsiAnnotationMemberValue existing = psiAnnotation.findDeclaredAttributeValue(attributeName);
493     if (value == null) {
494       if (existing == null) {
495         return null;
496       }
497       existing.getParent().delete();
498     }
499     else {
500       if (existing != null) {
501         ((PsiNameValuePair)existing.getParent()).setValue(value);
502       }
503       else {
504         PsiNameValuePair[] attributes = psiAnnotation.getParameterList().getAttributes();
505         if (attributes.length == 1) {
506           PsiNameValuePair attribute = attributes[0];
507           if (attribute.getName() == null) {
508             PsiAnnotationMemberValue defValue = attribute.getValue();
509             assert defValue != null : attribute;
510             attribute.replace(createNameValuePair(defValue, PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME + "=", annotationCreator));
511           }
512         }
513
514         boolean allowNoName = attributes.length == 0 && ("value".equals(attributeName) || null == attributeName);
515         final String namePrefix = allowNoName ? "" : attributeName + "=";
516         psiAnnotation.getParameterList().addBefore(createNameValuePair(value, namePrefix, annotationCreator), null);
517       }
518     }
519     return psiAnnotation.findDeclaredAttributeValue(attributeName);
520   }
521
522   private static PsiNameValuePair createNameValuePair(@NotNull PsiAnnotationMemberValue value,
523                                                      @NotNull String namePrefix,
524                                                      @NotNull PairFunction<? super Project, ? super String, ? extends PsiAnnotation> annotationCreator) {
525     return annotationCreator.fun(value.getProject(), "@A(" + namePrefix + value.getText() + ")").getParameterList().getAttributes()[0];
526   }
527
528   @Nullable
529   public static ASTNode skipWhitespaceAndComments(final ASTNode node) {
530     return TreeUtil.skipWhitespaceAndComments(node, true);
531   }
532
533   @Nullable
534   public static ASTNode skipWhitespaceCommentsAndTokens(final ASTNode node, @NotNull TokenSet alsoSkip) {
535     return TreeUtil.skipWhitespaceCommentsAndTokens(node, alsoSkip, true);
536   }
537
538   public static boolean isWhitespaceOrComment(ASTNode element) {
539     return TreeUtil.isWhitespaceOrComment(element);
540   }
541
542   @Nullable
543   public static ASTNode skipWhitespaceAndCommentsBack(final ASTNode node) {
544     if (node == null) return null;
545     if (!isWhitespaceOrComment(node)) return node;
546
547     ASTNode parent = node.getTreeParent();
548     ASTNode prev = node;
549     while (prev instanceof CompositeElement) {
550       if (!isWhitespaceOrComment(prev)) return prev;
551       prev = prev.getTreePrev();
552     }
553     if (prev == null) return null;
554     ASTNode firstChildNode = parent.getFirstChildNode();
555     ASTNode lastRelevant = null;
556     while (firstChildNode != prev) {
557       if (!isWhitespaceOrComment(firstChildNode)) lastRelevant = firstChildNode;
558       firstChildNode = firstChildNode.getTreeNext();
559     }
560     return lastRelevant;
561   }
562
563   @Nullable
564   public static ASTNode findStatementChild(@NotNull CompositePsiElement statement) {
565     if (DebugUtil.CHECK_INSIDE_ATOMIC_ACTION_ENABLED) {
566       ApplicationManager.getApplication().assertReadAccessAllowed();
567     }
568     for (ASTNode element = statement.getFirstChildNode(); element != null; element = element.getTreeNext()) {
569       if (element.getPsi() instanceof PsiStatement) return element;
570     }
571     return null;
572   }
573
574   @NotNull
575   public static PsiStatement[] getChildStatements(@NotNull CompositeElement psiCodeBlock) {
576     ApplicationManager.getApplication().assertReadAccessAllowed();
577     // no lock is needed because all chameleons are expanded already
578     int count = 0;
579     for (ASTNode child1 = psiCodeBlock.getFirstChildNode(); child1 != null; child1 = child1.getTreeNext()) {
580       if (child1.getPsi() instanceof PsiStatement) {
581         count++;
582       }
583     }
584
585     PsiStatement[] result = PsiStatement.ARRAY_FACTORY.create(count);
586     if (count == 0) return result;
587     int idx = 0;
588     for (ASTNode child = psiCodeBlock.getFirstChildNode(); child != null && idx < count; child = child.getTreeNext()) {
589       PsiElement element = child.getPsi();
590       if (element instanceof PsiStatement) {
591         result[idx++] = (PsiStatement)element;
592       }
593     }
594     return result;
595   }
596
597   public static boolean isVarArgs(@NotNull PsiMethod method) {
598     PsiParameter[] parameters = method.getParameterList().getParameters();
599     return parameters.length > 0 && parameters[parameters.length - 1].isVarArgs();
600   }
601
602   public static PsiElement handleMirror(PsiElement element) {
603     return element instanceof PsiMirrorElement ? ((PsiMirrorElement)element).getPrototype() : element;
604   }
605
606   @Nullable
607   public static PsiModifierList findNeighbourModifierList(@NotNull PsiJavaCodeReferenceElement ref) {
608     PsiElement parent = PsiTreeUtil.skipParentsOfType(ref, PsiJavaCodeReferenceElement.class);
609     if (parent instanceof PsiTypeElement) {
610       PsiElement grandParent = parent.getParent();
611       if (grandParent instanceof PsiModifierListOwner) {
612         return ((PsiModifierListOwner)grandParent).getModifierList();
613       }
614     }
615
616     return null;
617   }
618
619   public static boolean isTypeAnnotation(@Nullable PsiElement element) {
620     return element instanceof PsiAnnotation && AnnotationTargetUtil.isTypeAnnotation((PsiAnnotation)element);
621   }
622
623   public static void collectTypeUseAnnotations(@NotNull PsiModifierList modifierList, @NotNull List<? super PsiAnnotation> annotations) {
624     for (PsiAnnotation annotation : modifierList.getAnnotations()) {
625       if (AnnotationTargetUtil.isTypeAnnotation(annotation)) {
626         annotations.add(annotation);
627       }
628     }
629   }
630
631   private static final Key<Boolean> TYPE_ANNO_MARK = Key.create("type.annotation.mark");
632
633   public static void markTypeAnnotations(@NotNull PsiTypeElement typeElement) {
634     PsiElement left = PsiTreeUtil.skipSiblingsBackward(typeElement, PsiComment.class, PsiWhiteSpace.class, PsiTypeParameterList.class);
635     if (left instanceof PsiModifierList) {
636       for (PsiAnnotation annotation : ((PsiModifierList)left).getAnnotations()) {
637         if (AnnotationTargetUtil.isTypeAnnotation(annotation)) {
638           annotation.putUserData(TYPE_ANNO_MARK, Boolean.TRUE);
639         }
640       }
641     }
642   }
643
644   public static void deleteTypeAnnotations(@NotNull PsiTypeElement typeElement) {
645     PsiElement left = PsiTreeUtil.skipSiblingsBackward(typeElement, PsiComment.class, PsiWhiteSpace.class, PsiTypeParameterList.class);
646     if (left instanceof PsiModifierList) {
647       for (PsiAnnotation annotation : ((PsiModifierList)left).getAnnotations()) {
648         if (TYPE_ANNO_MARK.get(annotation) == Boolean.TRUE) {
649           annotation.delete();
650         }
651       }
652     }
653   }
654
655   @Contract("null -> false")
656   public static boolean isUnqualifiedReference(@Nullable PsiExpression expression) {
657     return expression instanceof PsiReferenceExpression && ((PsiReferenceExpression)expression).getQualifierExpression() == null;
658   }
659
660   @Nullable
661   public static PsiLoopStatement findEnclosingLoop(@NotNull PsiElement start) {
662     for (PsiElement e = start; !isCodeBoundary(e); e = e.getParent()) {
663       if (e instanceof PsiLoopStatement) return (PsiLoopStatement)e;
664     }
665     return null;
666   }
667
668   @Nullable
669   public static PsiElement findEnclosingSwitchOrLoop(@NotNull PsiElement start) {
670     for (PsiElement e = start; !isCodeBoundary(e); e = e.getParent()) {
671       if (e instanceof PsiSwitchBlock || e instanceof PsiLoopStatement) return e;
672     }
673     return null;
674   }
675
676   @Nullable
677   public static PsiLabeledStatement findEnclosingLabeledStatement(@NotNull PsiElement start, @NotNull String label) {
678     for (PsiElement e = start; !isCodeBoundary(e); e = e.getParent()) {
679       if (e instanceof PsiLabeledStatement && label.equals(((PsiLabeledStatement)e).getName())) return (PsiLabeledStatement)e;
680     }
681     return null;
682   }
683
684   @NotNull
685   public static List<String> findAllEnclosingLabels(@NotNull PsiElement start) {
686     List<String> result = new SmartList<>();
687     for (PsiElement context = start; !isCodeBoundary(context); context = context.getContext()) {
688       if (context instanceof PsiLabeledStatement) {
689         result.add(((PsiLabeledStatement)context).getName());
690       }
691     }
692     return result;
693   }
694
695   private static boolean isCodeBoundary(@Nullable PsiElement e) {
696     return e == null || e instanceof PsiMethod || e instanceof PsiClassInitializer || e instanceof PsiLambdaExpression;
697   }
698
699   /**
700    * Returns enclosing label statement for given label expression
701    *
702    * @param expression switch label expression
703    * @return enclosing label statement or null if given expression is not a label statement expression
704    */
705   @Nullable
706   public static PsiSwitchLabelStatementBase getSwitchLabel(@NotNull PsiExpression expression) {
707     PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent());
708     if (parent instanceof PsiExpressionList) {
709       PsiElement grand = parent.getParent();
710       if (grand instanceof PsiSwitchLabelStatementBase) {
711         return (PsiSwitchLabelStatementBase)grand;
712       }
713     }
714     return null;
715   }
716
717   public static boolean isLeafElementOfType(@Nullable PsiElement element, @NotNull IElementType type) {
718     return element instanceof LeafElement && ((LeafElement)element).getElementType() == type;
719   }
720
721   public static boolean isLeafElementOfType(PsiElement element, @NotNull TokenSet tokenSet) {
722     return element instanceof LeafElement && tokenSet.contains(((LeafElement)element).getElementType());
723   }
724
725   public static PsiType buildTypeFromTypeString(@NotNull final String typeName, @NotNull final PsiElement context, @NotNull final PsiFile psiFile) {
726     final PsiManager psiManager = psiFile.getManager();
727
728     if (typeName.indexOf('<') != -1 || typeName.indexOf('[') != -1 || typeName.indexOf('.') == -1) {
729       try {
730         return JavaPsiFacade.getElementFactory(psiManager.getProject()).createTypeFromText(typeName, context);
731       }
732       catch(Exception ignored) { } // invalid syntax will produce unresolved class type
733     }
734
735     PsiClass aClass = JavaPsiFacade.getInstance(psiManager.getProject()).findClass(typeName, context.getResolveScope());
736
737     PsiType resultType;
738     if (aClass == null) {
739       final LightClassReference ref = new LightClassReference(
740         psiManager,
741         PsiNameHelper.getShortClassName(typeName),
742         typeName,
743         PsiSubstitutor.EMPTY,
744         psiFile
745       );
746       resultType = new PsiClassReferenceType(ref, null);
747     } else {
748       PsiElementFactory factory = JavaPsiFacade.getElementFactory(psiManager.getProject());
749       PsiSubstitutor substitutor = factory.createRawSubstitutor(aClass);
750       resultType = factory.createType(aClass, substitutor);
751     }
752
753     return resultType;
754   }
755
756   @NotNull
757   public static <T extends PsiJavaCodeReferenceElement> JavaResolveResult[] multiResolveImpl(@NotNull T element,
758                                                                                              boolean incompleteCode,
759                                                                                              @NotNull ResolveCache.PolyVariantContextResolver<? super T> resolver) {
760     FileASTNode fileElement = SharedImplUtil.findFileElement(element.getNode());
761     if (fileElement == null) {
762       PsiUtilCore.ensureValid(element);
763       LOG.error("fileElement == null!");
764       return JavaResolveResult.EMPTY_ARRAY;
765     }
766     PsiFile psiFile = SharedImplUtil.getContainingFile(fileElement);
767     PsiManager manager = psiFile == null ? null : psiFile.getManager();
768     if (manager == null) {
769       PsiUtilCore.ensureValid(element);
770       LOG.error("getManager() == null!");
771       return JavaResolveResult.EMPTY_ARRAY;
772     }
773     boolean valid = psiFile.isValid();
774     if (!valid) {
775       PsiUtilCore.ensureValid(element);
776       LOG.error("psiFile.isValid() == false!");
777       return JavaResolveResult.EMPTY_ARRAY;
778     }
779     if (element instanceof PsiMethodReferenceExpression) {
780       // method refs: do not cache results during parent conflict resolving, acceptable checks, etc
781       if (ThreadLocalTypes.hasBindingFor(element)) {
782         return (JavaResolveResult[])resolver.resolve(element, psiFile, incompleteCode);
783       }
784     }
785
786     return multiResolveImpl(manager.getProject(), psiFile, element, incompleteCode, resolver);
787   }
788
789   @NotNull
790   public static <T extends PsiJavaCodeReferenceElement> JavaResolveResult[] multiResolveImpl(@NotNull Project project,
791                                                                                              @NotNull PsiFile psiFile,
792                                                                                              @NotNull T element,
793                                                                                              boolean incompleteCode,
794                                                                                              @NotNull ResolveCache.PolyVariantContextResolver<? super T> resolver) {
795     ResolveResult[] results = ResolveCache.getInstance(project).resolveWithCaching(element, resolver, true, incompleteCode, psiFile);
796     return results.length == 0 ? JavaResolveResult.EMPTY_ARRAY : (JavaResolveResult[])results;
797   }
798
799   public static VirtualFile getModuleVirtualFile(@NotNull PsiJavaModule module) {
800     return module instanceof LightJavaModule ? ((LightJavaModule)module).getRootVirtualFile() : module.getContainingFile().getVirtualFile();
801   }
802 }