[groovy] make GrTraitType extend PsiIntersectionType. Tests included.
[idea/community.git] / plugins / groovy / groovy-psi / src / org / jetbrains / plugins / groovy / lang / resolve / ResolveUtil.java
1 /*
2  * Copyright 2000-2015 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
17 package org.jetbrains.plugins.groovy.lang.resolve;
18
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.progress.ProgressManager;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.util.Key;
23 import com.intellij.openapi.util.Pair;
24 import com.intellij.psi.*;
25 import com.intellij.psi.scope.JavaScopeProcessorEvent;
26 import com.intellij.psi.scope.NameHint;
27 import com.intellij.psi.scope.PsiScopeProcessor;
28 import com.intellij.psi.util.*;
29 import com.intellij.util.PairProcessor;
30 import com.intellij.util.containers.ContainerUtil;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33 import org.jetbrains.plugins.groovy.GroovyLanguage;
34 import org.jetbrains.plugins.groovy.findUsages.LiteralConstructorReference;
35 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
36 import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
37 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
38 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
39 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
40 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
41 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
42 import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature;
43 import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
44 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
45 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
46 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
47 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
48 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
49 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
50 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression;
51 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty;
52 import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
53 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition;
54 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
55 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
56 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
57 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
58 import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
59 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
60 import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
61 import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
62 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
63 import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
64 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
65 import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrBindingVariable;
66 import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter;
67 import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrScriptField;
68 import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
69 import org.jetbrains.plugins.groovy.lang.psi.util.GdkMethodUtil;
70 import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
71 import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
72 import org.jetbrains.plugins.groovy.lang.resolve.processors.*;
73
74 import java.util.*;
75
76 /**
77  * @author ven
78  */
79 public class ResolveUtil {
80   private static final Logger LOG = Logger.getInstance(ResolveUtil.class);
81
82   public static final PsiScopeProcessor.Event DECLARATION_SCOPE_PASSED = new PsiScopeProcessor.Event() {};
83
84   private ResolveUtil() {
85   }
86
87   /**
88    *
89    * @param place - place to start tree walk up
90    * @param processor
91    * @param processNonCodeMethods - this parameter tells us if we need non code members
92    * @return
93    */
94   public static boolean treeWalkUp(@NotNull final PsiElement place,
95                                    @NotNull final PsiScopeProcessor processor,
96                                    boolean processNonCodeMethods) {
97     return treeWalkUp(place, place, processor, processNonCodeMethods, ResolveState.initial());
98   }
99
100   /**
101    *
102    * @param place - place to start tree walk up
103    * @param processor
104    * @param processNonCodeMethods - this parameter tells us if we need non code members
105    * @param state
106    * @return
107    */
108   public static boolean treeWalkUp(@NotNull final PsiElement place,
109                                    @NotNull final PsiElement originalPlace,
110                                    @NotNull final PsiScopeProcessor processor,
111                                    boolean processNonCodeMethods,
112                                    @NotNull final ResolveState state) {
113     try {
114     ClassHint hint = processor.getHint(ClassHint.KEY);
115     if (hint != null) {
116       return new DeclarationCacheKey(getNameHint(processor), hint, processNonCodeMethods, originalPlace).processCachedDeclarations(place, processor);
117     }
118
119     final PsiScopeProcessor nonCodeProcessor = processNonCodeMethods ? processor : null;
120       return doTreeWalkUp(place, originalPlace, processor, nonCodeProcessor, state);
121     }
122     catch (StackOverflowError e) {
123       LOG.error("StackOverflow", e, place.getContainingFile().getText());
124       throw e;
125     }
126   }
127
128   public static boolean doTreeWalkUp(@NotNull final PsiElement place,
129                                      @NotNull final PsiElement originalPlace,
130                                      @NotNull final PsiScopeProcessor processor,
131                                      @Nullable final PsiScopeProcessor nonCodeProcessor,
132                                      @NotNull final ResolveState state) {
133     final GrClosableBlock maxScope = nonCodeProcessor != null ? PsiTreeUtil.getParentOfType(place, GrClosableBlock.class, true, PsiFile.class) : null;
134
135     return PsiTreeUtil.treeWalkUp(place, maxScope, new PairProcessor<PsiElement, PsiElement>() {
136       @Override
137       public boolean process(PsiElement scope, PsiElement lastParent) {
138         ProgressManager.checkCanceled();
139         if (!doProcessDeclarations(originalPlace, lastParent, scope, substituteProcessor(processor, scope), nonCodeProcessor, state)) {
140           return false;
141         }
142         issueLevelChangeEvents(processor, scope);
143         return true;
144       }
145     });
146   }
147
148   static boolean doProcessDeclarations(@NotNull PsiElement place,
149                                        @Nullable PsiElement lastParent,
150                                        @NotNull PsiElement scope,
151                                        @NotNull PsiScopeProcessor plainProcessor,
152                                        @Nullable PsiScopeProcessor nonCodeProcessor,
153                                        @NotNull ResolveState state) {
154     if (scope instanceof GrClosableBlock && nonCodeProcessor != null) {
155       if (!((GrClosableBlock)scope).processClosureDeclarations(plainProcessor, nonCodeProcessor, state, lastParent, place)) return false;
156     }
157     else {
158       if (!scope.processDeclarations(plainProcessor, state, lastParent, place)) return false;
159
160       if (scope instanceof GrTypeDefinition || scope instanceof GrClosableBlock) {
161         if (!processStaticImports(plainProcessor, place.getContainingFile(), state, place)) return false;
162       }
163     }
164
165     if (nonCodeProcessor != null) {
166       if (!processScopeNonCodeMembers(place, lastParent, nonCodeProcessor, scope, state)) return false;
167     }
168     return true;
169   }
170
171   static void issueLevelChangeEvents(PsiScopeProcessor processor, PsiElement run) {
172     processor.handleEvent(JavaScopeProcessorEvent.CHANGE_LEVEL, null);
173     if (run instanceof GrClosableBlock && GrClosableBlock.OWNER_NAME.equals(getNameHint(processor)) ||
174         run instanceof PsiClass && !(run instanceof PsiAnonymousClass) ||
175         run instanceof GrMethod && run.getParent() instanceof GroovyFile) {
176       processor.handleEvent(DECLARATION_SCOPE_PASSED, run);
177     }
178   }
179
180   static PsiScopeProcessor substituteProcessor(PsiScopeProcessor processor, PsiElement scope) {
181     //hack for walking up in java code
182     //java's processDeclarations don't check names so we should do it manually
183     if (scope.getLanguage() != GroovyLanguage.INSTANCE && processor.getHint(NameHint.KEY) != null) {
184       return new JavaResolverProcessor(processor);
185     }
186     return processor;
187   }
188
189   private static boolean processScopeNonCodeMembers(@NotNull PsiElement place,
190                                                     @Nullable PsiElement lastParent,
191                                                     @NotNull PsiScopeProcessor processor,
192                                                     @NotNull PsiElement scope,
193                                                     @NotNull ResolveState state) {
194     //state = ResolveState.initial();
195     if (scope instanceof GrTypeDefinition) {
196       if (!processNonCodeMembers(createPsiType((GrTypeDefinition)scope), processor, place, state)) return false;
197
198       //@Category(CategoryType)
199       //class Scope {...}
200       PsiClassType categoryType = GdkMethodUtil.getCategoryType((PsiClass)scope);
201       if (categoryType != null) {
202         if (!processNonCodeMembers(categoryType, processor, place, state)) return false;
203       }
204
205     }
206
207     if (scope instanceof GroovyFileBase && ((GroovyFileBase)scope).isScript()) {
208       final PsiClass psiClass = ((GroovyFileBase)scope).getScriptClass();
209       if (psiClass != null) {
210         if (!processNonCodeMembers(createPsiType(psiClass), processor, place, state)) return false;
211       }
212     }
213
214     if (scope instanceof GrClosableBlock) {
215       ResolveState _state = state.put(ClassHint.RESOLVE_CONTEXT, scope);
216
217       PsiClass superClass = getLiteralSuperClass((GrClosableBlock)scope);
218       if (superClass != null && !superClass.processDeclarations(processor, _state, null, place)) return false;
219
220       if (!GdkMethodUtil.categoryIteration((GrClosableBlock)scope, processor, _state)) return false;
221       if (!processNonCodeMembers(GrClosureType.create(((GrClosableBlock)scope), false), processor, place, _state)) return false;
222     }
223
224     if (scope instanceof GrStatementOwner) {
225       if (!GdkMethodUtil.processMixinToMetaclass((GrStatementOwner)scope, processor, state, lastParent, place)) return false;
226     }
227
228     return true;
229   }
230
231   @NotNull
232   private static PsiClassType createPsiType(@NotNull PsiClass psiClass) {
233     PsiElementFactory factory = JavaPsiFacade.getElementFactory(psiClass.getProject());
234     return factory.createType(psiClass);
235   }
236
237   public static boolean processChildren(@NotNull PsiElement element,
238                                         @NotNull PsiScopeProcessor processor,
239                                         @NotNull ResolveState state,
240                                         @Nullable PsiElement lastParent,
241                                         @NotNull PsiElement place) {
242     if (!shouldProcessProperties(processor.getHint(ClassHint.KEY))) return true;
243
244     PsiElement run = lastParent == null ? element.getLastChild() : lastParent.getPrevSibling();
245     while (run != null) {
246       if (!run.processDeclarations(processor, state, null, place)) return false;
247       run = run.getPrevSibling();
248     }
249
250     return true;
251   }
252
253   @Nullable
254   public static String getNameHint(PsiScopeProcessor processor) {
255     NameHint nameHint = processor.getHint(NameHint.KEY);
256     if (nameHint == null) {
257       return null;
258     }
259
260     return nameHint.getName(ResolveState.initial());
261   }
262
263   public static boolean processElement(@NotNull PsiScopeProcessor processor,
264                                        @NotNull PsiNamedElement namedElement,
265                                        @NotNull ResolveState state) {
266     NameHint nameHint = processor.getHint(NameHint.KEY);
267     String name = nameHint == null ? null : nameHint.getName(state);
268     if (name == null || name.equals(namedElement.getName())) {
269       return processor.execute(namedElement, state);
270     }
271
272     return true;
273   }
274
275   public static boolean processAllDeclarations(@NotNull PsiType type,
276                                                @NotNull PsiScopeProcessor processor,
277                                                @NotNull ResolveState state,
278                                                @NotNull PsiElement place) {
279     return processAllDeclarationsSeparately(type, processor, processor, state, place);
280   }
281
282   public static boolean processAllDeclarationsSeparately(@NotNull PsiType type,
283                                                          @NotNull PsiScopeProcessor processor,
284                                                          @NotNull PsiScopeProcessor nonCodeProcessor,
285                                                          @NotNull ResolveState state,
286                                                          @NotNull PsiElement place) {
287     type = TypesUtil.boxPrimitiveType(type,place.getManager(),place.getResolveScope());
288     if (type instanceof PsiClassType) {
289       final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
290       final PsiClass psiClass = resolveResult.getElement();
291       final PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
292       state = state.put(PsiSubstitutor.KEY, substitutor.putAll(resolveResult.getSubstitutor()));
293       if (psiClass != null) {
294         if (!psiClass.processDeclarations(processor, state, null, place)) return false;
295       }
296     }
297     if (!processCategoryMembers(place, nonCodeProcessor, state)) return false;
298     if (!processNonCodeMembers(type, nonCodeProcessor, place, state)) return false;
299     return true;
300   }
301
302   public static boolean processNonCodeMembers(@NotNull PsiType type,
303                                               @NotNull PsiScopeProcessor processor,
304                                               @NotNull PsiElement place,
305                                               @NotNull ResolveState state) {
306     if (type instanceof PsiEllipsisType) {
307       type = ((PsiEllipsisType)type).toArrayType();
308     }
309     if (!NonCodeMembersContributor.runContributors(type, processor, place, state)) {
310       return false;
311     }
312
313     return true;
314   }
315
316   private static final Key<PsiType> COMPARABLE = Key.create(CommonClassNames.JAVA_LANG_COMPARABLE);
317   private static final Key<PsiType> SERIALIZABLE = Key.create(CommonClassNames.JAVA_IO_SERIALIZABLE);
318   private static final Key<PsiType> STRING = Key.create(CommonClassNames.JAVA_LANG_STRING);
319
320   private static void collectSuperTypes(PsiType type, Set<String> visited, Project project) {
321     String qName = rawCanonicalText(type);
322
323     if (!visited.add(qName)) {
324       return;
325     }
326
327     final PsiType[] superTypes = type.getSuperTypes();
328     for (PsiType superType : superTypes) {
329       collectSuperTypes(TypeConversionUtil.erasure(superType), visited, project);
330     }
331
332     if (type instanceof PsiArrayType && superTypes.length == 0) {
333       PsiType comparable = createTypeFromText(project, COMPARABLE, CommonClassNames.JAVA_LANG_COMPARABLE);
334       PsiType serializable = createTypeFromText(project, SERIALIZABLE, CommonClassNames.JAVA_IO_SERIALIZABLE);
335       collectSuperTypes(comparable, visited, project);
336       collectSuperTypes(serializable, visited, project);
337     }
338
339     if (GroovyCommonClassNames.GROOVY_LANG_GSTRING.equals(qName)) {
340       collectSuperTypes(createTypeFromText(project, STRING, CommonClassNames.JAVA_LANG_STRING), visited, project);
341     }
342
343   }
344
345   public static PsiType createTypeFromText(Project project, Key<PsiType> key, String text) {
346     final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
347     PsiType type = project.getUserData(key);
348     if (type == null) {
349       type = factory.createTypeFromText(text, null);
350       project.putUserData(key, type);
351     }
352     return type;
353   }
354
355   public static Set<String> getAllSuperTypes(@NotNull PsiType base, final Project project) {
356     final Map<String, Set<String>> cache =
357       CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider<Map<String, Set<String>>>() {
358         @Override
359         public Result<Map<String, Set<String>>> compute() {
360           final Map<String, Set<String>> result = ContainerUtil.newConcurrentMap();
361           return Result.create(result, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
362         }
363       });
364
365     final PsiClass cls = PsiUtil.resolveClassInType(base);
366     String key;
367     if (cls instanceof PsiTypeParameter) {
368       final PsiClass superClass = cls.getSuperClass();
369       key = cls.getName() + (superClass == null ? CommonClassNames.JAVA_LANG_OBJECT : superClass.getName());
370     }
371     else if (base instanceof PsiClassType) {
372       key = TypesUtil.getQualifiedName(base);
373     }
374     else {
375       key = base.getCanonicalText();
376     }
377     Set<String> result = key == null ? null : cache.get(key);
378     if (result == null) {
379       result = ContainerUtil.newHashSet();
380       collectSuperTypes(base, result, project);
381       if (key != null) {
382         cache.put(key, result);
383       }
384     }
385     return result;
386   }
387
388   @NotNull
389   private static String rawCanonicalText(@NotNull PsiType type) {
390     if (type instanceof PsiClassType) {
391       String qname = TypesUtil.getQualifiedName(type);
392       if (qname != null) {
393         return qname;
394       }
395     }
396     return TypeConversionUtil.erasure(type).getCanonicalText();
397   }
398
399   public static GroovyPsiElement resolveProperty(GroovyPsiElement place, String name) {
400     PropertyResolverProcessor processor = new PropertyResolverProcessor(name, place);
401     return resolveExistingElement(place, processor, GrVariable.class, GrReferenceExpression.class);
402   }
403
404   @Nullable
405   public static PsiClass resolveClass(GroovyPsiElement place, String name) {
406     ClassResolverProcessor processor = new ClassResolverProcessor(name, place);
407     return resolveExistingElement(place, processor, PsiClass.class);
408   }
409
410   @Nullable
411   public static <T> T resolveExistingElement(PsiElement place, ResolverProcessor processor, Class<? extends T>... classes) {
412     treeWalkUp(place, processor, true);
413     final GroovyResolveResult[] candidates = processor.getCandidates();
414     for (GroovyResolveResult candidate : candidates) {
415       final PsiElement element = candidate.getElement();
416       if (element == place) continue;
417       for (Class<? extends T> clazz : classes) {
418         if (clazz.isInstance(element)) //noinspection unchecked
419           return (T)element;
420       }
421     }
422
423     return null;
424   }
425
426   @NotNull
427   public static Pair<GrStatement, GrLabeledStatement> resolveLabelTargets(@Nullable String labelName,
428                                                                           @Nullable PsiElement element,
429                                                                           boolean isBreak) {
430     if (element == null) return new Pair<GrStatement, GrLabeledStatement>(null, null);
431
432     if (labelName == null) {
433       do {
434         element = element.getContext();
435         if (element == null || element instanceof GrClosableBlock || element instanceof GrMember || element instanceof GroovyFile) {
436           return new Pair<GrStatement, GrLabeledStatement>(null, null);
437         }
438       }
439       while (!(element instanceof GrLoopStatement) && !(isBreak && element instanceof GrSwitchStatement));
440       return new Pair<GrStatement, GrLabeledStatement>(((GrStatement)element), null);
441     }
442
443     GrStatement statement = null;
444     do {
445       PsiElement last = element;
446       element = element.getContext();
447       if (element == null || element instanceof GrMember || element instanceof GroovyFile) break;
448       if (element instanceof GrStatement && !(element instanceof GrClosableBlock)) {
449         statement = (GrStatement)element;
450       }
451       PsiElement sibling = element;
452       while (sibling != null) {
453         final GrLabeledStatement labelStatement = findLabelStatementIn(sibling, last, labelName);
454         if (labelStatement != null) {
455           return Pair.create(statement, labelStatement);
456         }
457         sibling = sibling.getPrevSibling();
458       }
459       if (element instanceof GrClosableBlock) break;
460     }
461     while (true);
462     return new Pair<GrStatement, GrLabeledStatement>(null, null);
463   }
464
465   private static boolean isApplicableLabelStatement(PsiElement element, String labelName) {
466     return ((element instanceof GrLabeledStatement && labelName.equals(((GrLabeledStatement)element).getName())));
467   }
468
469   @Nullable
470   private static GrLabeledStatement findLabelStatementIn(PsiElement element, PsiElement lastChild, String labelName) {
471     if (isApplicableLabelStatement(element, labelName)) {
472       return (GrLabeledStatement)element;
473     }
474     for (PsiElement child = element.getFirstChild(); child != null && child != lastChild; child = child.getNextSibling()) {
475       final GrLabeledStatement statement = findLabelStatementIn(child, child, labelName);
476       if (statement != null) return statement;
477     }
478     return null;
479   }
480
481   @Nullable
482   public static GrLabeledStatement resolveLabeledStatement(@Nullable String labelName, @Nullable PsiElement element, boolean isBreak) {
483     return resolveLabelTargets(labelName, element, isBreak).second;
484   }
485
486   @Nullable
487   public static GrStatement resolveLabelTargetStatement(@Nullable String labelName, @Nullable PsiElement element, boolean isBreak) {
488     return resolveLabelTargets(labelName, element, isBreak).first;
489   }
490
491   public static boolean processCategoryMembers(@NotNull PsiElement place,
492                                                @NotNull PsiScopeProcessor processor,
493                                                @NotNull ResolveState state) {
494     boolean inCodeBlock = true;
495     PsiElement run = place;
496     PsiElement lastParent = null;
497
498     while (run != null) {
499       ProgressManager.checkCanceled();
500       if (run instanceof GrMember) {
501         inCodeBlock = false;
502       }
503       if (run instanceof GrClosableBlock) {
504         if (inCodeBlock) {
505           if (!GdkMethodUtil.categoryIteration((GrClosableBlock)run, processor, state)) return false;
506         }
507
508         PsiClass superClass = getLiteralSuperClass((GrClosableBlock)run);
509         if (superClass != null && !GdkMethodUtil.processCategoryMethods(run, processor, state, superClass)) return false;
510       }
511
512       if (run instanceof GrStatementOwner) {
513         if (!GdkMethodUtil.processMixinToMetaclass((GrStatementOwner)run, processor, state, lastParent, place)) return false;
514       }
515
516       lastParent = run;
517       run = run.getContext();
518     }
519
520     return true;
521   }
522
523   @Nullable
524   private static PsiClass getLiteralSuperClass(GrClosableBlock closure) {
525     PsiClassType type;
526     if (closure.getParent() instanceof GrNamedArgument && closure.getParent().getParent() instanceof GrListOrMap) {
527       type = LiteralConstructorReference.getTargetConversionType((GrListOrMap)closure.getParent().getParent());
528     }
529     else {
530       type = LiteralConstructorReference.getTargetConversionType(closure);
531     }
532     return type != null ? type.resolve() : null;
533   }
534
535   public static PsiElement[] mapToElements(GroovyResolveResult[] candidates) {
536     PsiElement[] elements = new PsiElement[candidates.length];
537     for (int i = 0; i < elements.length; i++) {
538       elements[i] = candidates[i].getElement();
539     }
540
541     return elements;
542   }
543
544   public static GroovyResolveResult[] filterSameSignatureCandidates(Collection<GroovyResolveResult> candidates) {
545     GroovyResolveResult[] array = candidates.toArray(new GroovyResolveResult[candidates.size()]);
546     if (array.length == 1) return array;
547
548     List<GroovyResolveResult> result = new ArrayList<GroovyResolveResult>();
549     result.add(array[0]);
550
551     Outer:
552     for (int i = 1; i < array.length; i++) {
553       PsiElement currentElement = array[i].getElement();
554       if (currentElement instanceof PsiMethod) {
555         PsiMethod currentMethod = (PsiMethod)currentElement;
556         for (Iterator<GroovyResolveResult> iterator = result.iterator(); iterator.hasNext();) {
557           final GroovyResolveResult otherResolveResult = iterator.next();
558           PsiElement element = otherResolveResult.getElement();
559           if (element instanceof PsiMethod) {
560             PsiMethod method = (PsiMethod)element;
561             if (dominated(currentMethod, array[i].getSubstitutor(), method, otherResolveResult.getSubstitutor())) {
562               continue Outer;
563             }
564             else if (dominated(method, otherResolveResult.getSubstitutor(), currentMethod, array[i].getSubstitutor())) {
565               iterator.remove();
566             }
567           }
568         }
569       }
570
571       result.add(array[i]);
572     }
573
574     return result.toArray(new GroovyResolveResult[result.size()]);
575   }
576
577   public static boolean dominated(PsiMethod method1,
578                                   PsiSubstitutor substitutor1,
579                                   PsiMethod method2,
580                                   PsiSubstitutor substitutor2) {  //method1 has more general parameter types then method2
581     if (!method1.getName().equals(method2.getName())) return false;
582
583     PsiParameter[] params1 = method1.getParameterList().getParameters();
584     PsiParameter[] params2 = method2.getParameterList().getParameters();
585
586     if (params1.length != params2.length) return false;
587
588     for (int i = 0; i < params2.length; i++) {
589       PsiType type1 = substitutor1.substitute(params1[i].getType());
590       PsiType type2 = substitutor2.substitute(params2[i].getType());
591       if (!type1.equals(type2)) return false;
592     }
593
594     if (method1 instanceof GrGdkMethod && method2 instanceof GrGdkMethod) {
595       PsiMethod static1 = ((GrGdkMethod)method1).getStaticMethod();
596       PsiMethod static2 = ((GrGdkMethod)method2).getStaticMethod();
597
598       PsiParameter p1 = static1.getParameterList().getParameters()[0];
599       PsiParameter p2 = static2.getParameterList().getParameters()[0];
600
601       PsiType t1 = substitutor1.substitute(p1.getType());
602       PsiType t2 = substitutor2.substitute(p2.getType());
603
604       if (!t1.equals(t2)) {
605         if (t1 instanceof PsiClassType) t1 = TypeConversionUtil.erasure(t1);
606         if (t2 instanceof PsiClassType) t2 = TypeConversionUtil.erasure(t2);
607         //method1 is more general than method2
608         return t1.isAssignableFrom(t2);
609       }
610     }
611
612     return true;
613   }
614
615   public static GroovyResolveResult[] getCallVariants(GroovyPsiElement place) {
616     final PsiElement parent = place.getParent();
617     GroovyResolveResult[] variants = GroovyResolveResult.EMPTY_ARRAY;
618     if (parent instanceof GrCallExpression) {
619       variants = ((GrCallExpression)parent).getCallVariants(place instanceof GrExpression ? (GrExpression)place : null);
620     }
621     else if (parent instanceof GrConstructorInvocation) {
622       final PsiClass clazz = ((GrConstructorInvocation)parent).getDelegatedClass();
623       if (clazz != null) {
624         final PsiMethod[] constructors = clazz.getConstructors();
625         variants = getConstructorResolveResult(constructors, place);
626       }
627     }
628     else if (parent instanceof GrAnonymousClassDefinition) {
629       final PsiElement element = ((GrAnonymousClassDefinition)parent).getBaseClassReferenceGroovy().resolve();
630       if (element instanceof PsiClass) {
631         final PsiMethod[] constructors = ((PsiClass)element).getConstructors();
632         variants = getConstructorResolveResult(constructors, place);
633       }
634     }
635     else if (place instanceof GrReferenceExpression) {
636       variants = ((GrReferenceExpression)place).getSameNameVariants();
637     }
638     return variants;
639   }
640
641   private static GroovyResolveResult[] getConstructorResolveResult(PsiMethod[] constructors, PsiElement place) {
642     GroovyResolveResult[] variants = new GroovyResolveResult[constructors.length];
643     for (int i = 0; i < constructors.length; i++) {
644       final boolean isAccessible = PsiUtil.isAccessible(constructors[i], place, null);
645       variants[i] = new GroovyResolveResultImpl(constructors[i], isAccessible);
646     }
647     return variants;
648   }
649
650   public static GroovyResolveResult[] getAllClassConstructors(@NotNull PsiClass psiClass,
651                                                               @NotNull PsiSubstitutor substitutor,
652                                                               @Nullable PsiType[] argTypes,
653                                                               @NotNull PsiElement place) {
654     final MethodResolverProcessor processor = new MethodResolverProcessor(psiClass.getName(), place, true, null, argTypes, PsiType.EMPTY_ARRAY);
655     ResolveState state = ResolveState.initial().put(PsiSubstitutor.KEY, substitutor);
656     for (PsiMethod constructor : psiClass.getConstructors()) {
657       processor.execute(constructor, state);
658     }
659
660     final PsiClassType qualifierType = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass);
661     processNonCodeMembers(qualifierType, processor, place, state);
662     return processor.getCandidates();
663   }
664
665   public static boolean isKeyOfMap(GrReferenceExpression ref) {
666     if (!(ref.getParent() instanceof GrIndexProperty) && org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isCall(ref)) return false;
667     if (ref.multiResolve(false).length > 0) return false;
668     return mayBeKeyOfMap(ref);
669   }
670
671   public static boolean mayBeKeyOfMap(GrReferenceExpression ref) {
672     final GrExpression qualifier = getSelfOrWithQualifier(ref);
673     if (qualifier == null) return false;
674     if (qualifier instanceof GrReferenceExpression && ((GrReferenceExpression)qualifier).resolve() instanceof PsiClass) return false;
675     return InheritanceUtil.isInheritor(qualifier.getType(), CommonClassNames.JAVA_UTIL_MAP);
676   }
677
678
679   @Nullable
680   public static GrExpression getSelfOrWithQualifier(GrReferenceExpression ref) {
681     final GrExpression qualifier = ref.getQualifierExpression();
682     if (qualifier != null) {
683       return qualifier;
684     }
685
686     PsiElement place = ref;
687     while (true) {
688       final GrClosableBlock closure = PsiTreeUtil.getParentOfType(place, GrClosableBlock.class, true, GrMember.class, GroovyFile.class);
689       if (closure == null) break;
690       place = closure;
691       PsiElement clParent = closure.getParent();
692       if (clParent instanceof GrArgumentList) clParent = clParent.getParent();
693       if (!(clParent instanceof GrMethodCall)) continue;
694       final GrExpression expression = ((GrMethodCall)clParent).getInvokedExpression();
695       if (expression instanceof GrReferenceExpression &&
696           GdkMethodUtil.isWithName(((GrReferenceExpression)expression).getReferenceName()) &&
697           ((GrReferenceExpression)expression).resolve() instanceof GrGdkMethod) {
698         final GrExpression withQualifier = ((GrReferenceExpression)expression).getQualifierExpression();
699         if (withQualifier != null) {
700           return withQualifier;
701         }
702       }
703     }
704     return null;
705   }
706
707
708   @NotNull
709   public static GroovyResolveResult[] getMethodCandidates(@NotNull PsiType thisType,
710                                                           @Nullable String methodName,
711                                                           @NotNull PsiElement place,
712                                                           @Nullable PsiType... argumentTypes) {
713     return getMethodCandidates(thisType, methodName, place, true, false, false, argumentTypes);
714   }
715
716   @NotNull
717   public static GroovyResolveResult[] getMethodCandidates(@NotNull PsiType thisType,
718                                                           @Nullable String methodName,
719                                                           @NotNull PsiElement place,
720                                                           boolean resolveClosures,
721                                                           boolean allVariants,
722                                                           boolean byShape,
723                                                           @Nullable PsiType... argumentTypes) {
724     if (methodName == null) return GroovyResolveResult.EMPTY_ARRAY;
725     thisType = TypesUtil.boxPrimitiveType(thisType, place.getManager(), place.getResolveScope());
726     MethodResolverProcessor processor =
727       new MethodResolverProcessor(methodName, place, false, thisType, argumentTypes, PsiType.EMPTY_ARRAY, allVariants, byShape);
728     final ResolveState state = ResolveState.initial().put(ClassHint.RESOLVE_CONTEXT, place);
729     processAllDeclarations(thisType, processor, state, place);
730     boolean hasApplicableMethods = processor.hasApplicableCandidates();
731     final GroovyResolveResult[] methodCandidates = processor.getCandidates();
732     if (hasApplicableMethods && methodCandidates.length == 1) return methodCandidates;
733
734     final GroovyResolveResult[] allPropertyCandidates;
735     if (resolveClosures) {
736       PropertyResolverProcessor propertyResolver = new PropertyResolverProcessor(methodName, place);
737       processAllDeclarations(thisType, propertyResolver, state, place);
738       allPropertyCandidates = propertyResolver.getCandidates();
739     }
740     else {
741       allPropertyCandidates = GroovyResolveResult.EMPTY_ARRAY;
742     }
743
744     List<GroovyResolveResult> propertyCandidates = new ArrayList<GroovyResolveResult>(allPropertyCandidates.length);
745     for (GroovyResolveResult candidate : allPropertyCandidates) {
746       final PsiElement resolved = candidate.getElement();
747       if (!(resolved instanceof GrField)) continue;
748       final PsiType type = ((GrField)resolved).getTypeGroovy();
749       if (isApplicableClosureType(type, argumentTypes, place)) {
750         propertyCandidates.add(candidate);
751       }
752     }
753
754     for (GroovyResolveResult candidate : propertyCandidates) {
755       final PsiElement element = candidate.getElement();
756       if (element instanceof GrField) {
757         final PsiClass containingClass = ((PsiField)element).getContainingClass();
758         if (containingClass != null && PsiTreeUtil.isContextAncestor(containingClass, place, true)) {
759           return new GroovyResolveResult[]{candidate};
760         }
761       }
762     }
763
764     List<GroovyResolveResult> allCandidates = new ArrayList<GroovyResolveResult>();
765     if (hasApplicableMethods) {
766       ContainerUtil.addAll(allCandidates, methodCandidates);
767     }
768     ContainerUtil.addAll(allCandidates, propertyCandidates);
769
770     //search for getters
771     for (String getterName : GroovyPropertyUtils.suggestGettersName(methodName)) {
772       AccessorResolverProcessor getterResolver =
773         new AccessorResolverProcessor(getterName, methodName, place, true, false, thisType, PsiType.EMPTY_ARRAY);
774       processAllDeclarations(thisType, getterResolver, state, place);
775       final GroovyResolveResult[] candidates = getterResolver.getCandidates(); //can be only one candidate
776       final List<GroovyResolveResult> applicable = new ArrayList<GroovyResolveResult>();
777       for (GroovyResolveResult candidate : candidates) {
778         PsiMethod method = (PsiMethod)candidate.getElement();
779         assert method != null;
780         final PsiType type = org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getSmartReturnType(method);
781         if (isApplicableClosureType(type, argumentTypes, place)) {
782           applicable.add(candidate);
783         }
784       }
785       if (applicable.size() == 1) {
786         return applicable.toArray(new GroovyResolveResult[applicable.size()]);
787       }
788       ContainerUtil.addAll(allCandidates, applicable);
789     }
790
791     if (!allCandidates.isEmpty()) {
792       return allCandidates.toArray(new GroovyResolveResult[allCandidates.size()]);
793     }
794     else if (!hasApplicableMethods) {
795       return methodCandidates;
796     }
797     return GroovyResolveResult.EMPTY_ARRAY;
798   }
799
800   private static boolean isApplicableClosureType(@Nullable PsiType type, @Nullable PsiType[] argTypes, @NotNull PsiElement place) {
801     if (!(type instanceof GrClosureType)) return false;
802     if (argTypes == null) return true;
803
804     final GrSignature signature = ((GrClosureType)type).getSignature();
805     return GrClosureSignatureUtil.isSignatureApplicable(signature, argTypes, place);
806   }
807
808   @Nullable
809   public static PsiType extractReturnTypeFromCandidate(GroovyResolveResult candidate, GrExpression expression, @Nullable PsiType[] args) {
810     final PsiElement element = candidate.getElement();
811     if (element instanceof PsiMethod && !candidate.isInvokedOnProperty()) {
812       return TypesUtil.substituteAndNormalizeType(org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getSmartReturnType((PsiMethod)element),
813                                                   candidate.getSubstitutor(), candidate.getSpreadState(), expression);
814     }
815
816     final PsiType type;
817     if (element instanceof GrField) {
818       type = ((GrField)element).getTypeGroovy();
819     }
820     else if (element instanceof PsiMethod) {
821       type = org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getSmartReturnType((PsiMethod)element);
822     }
823     else {
824       return null;
825     }
826     if (type instanceof GrClosureType) {
827       final GrSignature signature = ((GrClosureType)type).getSignature();
828       PsiType returnType = GrClosureSignatureUtil.getReturnType(signature, args, expression);
829       return TypesUtil.substituteAndNormalizeType(returnType, candidate.getSubstitutor(), candidate.getSpreadState(), expression);
830     }
831     return null;
832   }
833
834   public static boolean isEnumConstant(PsiReference ref, String name, String qName) {
835     PsiElement resolved = ref.resolve();
836     if (!(resolved instanceof PsiEnumConstant)) return false;
837     if (!name.equals(((PsiEnumConstant)resolved).getName())) return false;
838
839     PsiClass aClass = ((PsiEnumConstant)resolved).getContainingClass();
840     if (aClass == null) return false;
841     return qName.equals(aClass.getQualifiedName());
842   }
843
844   public static boolean isScriptField(GrVariable var) {
845     PsiClass context = org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getContextClass(var.getParent());
846     final GrModifierList modifierList = var.getModifierList();
847     return context instanceof GroovyScriptClass &&
848            modifierList != null &&
849            modifierList.findAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_FIELD) != null;
850   }
851
852   @Nullable
853   public static PsiClass resolveAnnotation(PsiElement insideAnnotation) {
854     final GrAnnotation annotation = PsiTreeUtil.getParentOfType(insideAnnotation, GrAnnotation.class, false);
855     if (annotation == null) return null;
856
857     final GrCodeReferenceElement reference = annotation.getClassReference();
858     final GroovyResolveResult result = reference.advancedResolve();
859     final PsiElement element = result.getElement();
860     if (element instanceof PsiClass && ((PsiClass)element).isAnnotationType()) return (PsiClass)element;
861     return null;
862   }
863
864   @NotNull
865   public static String inferExpectedPackageName(PsiElement place) {
866     PsiFile file = place.getContainingFile();
867     PsiDirectory psiDirectory = file.getContainingDirectory();
868     if (psiDirectory != null && file instanceof GroovyFile) {
869       PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(psiDirectory);
870       if (aPackage != null) {
871         return aPackage.getQualifiedName();
872       }
873     }
874     return "";
875   }
876
877   public static PsiNamedElement findDuplicate(@NotNull GrVariable variable) {
878     if (isScriptField(variable)) {
879       final String name = variable.getName();
880
881       int count = 0;
882       final GroovyScriptClass script = (GroovyScriptClass)((GroovyFile)variable.getContainingFile()).getScriptClass();
883       assert script != null;
884       for (GrScriptField field : GrScriptField.getScriptFields(script)) {
885         if (name.equals(field.getName())) count++;
886       }
887
888       return count > 1 ? GrScriptField.getScriptField(variable) : null;
889     }
890     else {
891       PsiNamedElement duplicate = resolveExistingElement(variable, new DuplicateVariablesProcessor(variable), GrVariable.class);
892       final PsiElement context1 = variable.getContext();
893       if (duplicate == null && variable instanceof GrParameter && context1 != null) {
894         final PsiElement context = context1.getContext();
895         if (context instanceof GrClosableBlock ||
896             context instanceof GrMethod && !(context.getParent() instanceof GroovyFile) ||
897             context instanceof GrTryCatchStatement) {
898           duplicate = resolveExistingElement(context.getParent(), new DuplicateVariablesProcessor(variable), GrVariable.class);
899         }
900       }
901       if (duplicate instanceof GrLightParameter && "args".equals(duplicate.getName())) {
902         return null;
903       }
904       else {
905         return duplicate;
906       }
907     }
908   }
909
910   public static boolean canBeClassOrPackage(final GrReferenceExpression ref) {
911     GrExpression qualifier = ref.getQualifier();
912     if (qualifier instanceof GrReferenceExpression) {
913       final PsiElement resolvedQualifier = ((GrReferenceExpression)qualifier).resolve();
914       return resolvedQualifier instanceof PsiClass || resolvedQualifier instanceof PsiPackage;
915     }
916     else {
917       return qualifier == null;
918     }
919   }
920
921   @NotNull
922   public static List<Pair<PsiParameter, PsiType>> collectExpectedParamsByArg(@NotNull PsiElement place,
923                                                                              @NotNull GroovyResolveResult[] variants,
924                                                                              @NotNull GrNamedArgument[] namedArguments,
925                                                                              @NotNull GrExpression[] expressionArguments,
926                                                                              @NotNull GrClosableBlock[] closureArguments,
927                                                                              @NotNull GrExpression arg) {
928     List<Pair<PsiParameter, PsiType>> expectedParams = ContainerUtil.newArrayList();
929
930     for (GroovyResolveResult variant : variants) {
931       final Map<GrExpression, Pair<PsiParameter, PsiType>> map = GrClosureSignatureUtil.mapArgumentsToParameters(
932         variant, place, true, true, namedArguments, expressionArguments, closureArguments
933       );
934
935       if (map != null) {
936         final Pair<PsiParameter, PsiType> pair = map.get(arg);
937         ContainerUtil.addIfNotNull(expectedParams, pair);
938       }
939     }
940     return expectedParams;
941   }
942
943   public static boolean shouldProcessClasses(ClassHint classHint) {
944     return classHint == null || classHint.shouldProcess(ClassHint.ResolveKind.CLASS);
945   }
946
947   public static boolean shouldProcessMethods(ClassHint classHint) {
948     return classHint == null || classHint.shouldProcess(ClassHint.ResolveKind.METHOD);
949   }
950
951   public static boolean shouldProcessProperties(ClassHint classHint) {
952     return classHint == null || classHint.shouldProcess(ClassHint.ResolveKind.PROPERTY);
953   }
954
955   public static boolean shouldProcessPackages(ClassHint classHint) {
956     return classHint == null || classHint.shouldProcess(ClassHint.ResolveKind.PACKAGE);
957   }
958
959   public static boolean processStaticImports(@NotNull PsiScopeProcessor resolver,
960                                              @NotNull PsiFile file,
961                                              @NotNull ResolveState state,
962                                              @NotNull PsiElement place) {
963     if (!shouldProcessMethods(resolver.getHint(ClassHint.KEY))) return true;
964
965     return file.processDeclarations(new GrDelegatingScopeProcessorWithHints(resolver, null, ClassHint.RESOLVE_KINDS_METHOD) {
966       @Override
967       public boolean execute(@NotNull PsiElement element, @NotNull ResolveState _state) {
968         if (_state.get(RESOLVE_CONTEXT) instanceof GrImportStatement) {
969           super.execute(element, _state);
970         }
971         return true;
972       }
973     }, state, null, place);
974   }
975
976   public static boolean isClassReference(@NotNull GrReferenceExpression ref) {
977     GrExpression qualifier = ref.getQualifier();
978     return "class".equals(ref.getReferenceName()) &&
979            qualifier instanceof GrReferenceExpression &&
980            ((GrReferenceExpression)qualifier).resolve() instanceof PsiClass &&
981            !org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isThisReference(qualifier);
982   }
983
984   private static class DuplicateVariablesProcessor extends PropertyResolverProcessor {
985     private boolean myBorderPassed;
986     private final boolean myHasVisibilityModifier;
987
988     public DuplicateVariablesProcessor(GrVariable variable) {
989       super(variable.getName(), variable);
990       myBorderPassed = false;
991       myHasVisibilityModifier = hasExplicitVisibilityModifiers(variable);
992     }
993
994     private static boolean hasExplicitVisibilityModifiers(GrVariable variable) {
995       final GrModifierList modifierList = variable.getModifierList();
996       return modifierList != null && modifierList.hasExplicitVisibilityModifiers();
997     }
998
999     @Override
1000     public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
1001       if (myBorderPassed) {
1002         return false;
1003       }
1004       if (element instanceof GrVariable && hasExplicitVisibilityModifiers((GrVariable)element) != myHasVisibilityModifier) {
1005         return true;
1006       }
1007       if (element instanceof GrBindingVariable) return true;
1008       return super.execute(element, state);
1009     }
1010
1011     @Override
1012     public void handleEvent(@NotNull Event event, Object associated) {
1013       if (event == DECLARATION_SCOPE_PASSED) {
1014         myBorderPassed = true;
1015       }
1016       super.handleEvent(event, associated);
1017     }
1018   }
1019 }