IDEA-64291 groovy parsing get stuck and is wrong
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / resolve / ResolveUtil.java
1 /*
2  * Copyright 2000-2009 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.project.Project;
20 import com.intellij.openapi.roots.ProjectRootManager;
21 import com.intellij.openapi.util.Key;
22 import com.intellij.openapi.util.Pair;
23 import com.intellij.openapi.util.Ref;
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 org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31 import org.jetbrains.plugins.groovy.dsl.GroovyDslFileIndex;
32 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
33 import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
34 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
35 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
36 import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
37 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
38 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
39 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
40 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
41 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
42 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression;
43 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
44 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition;
45 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
46 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinitionBody;
47 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
48 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
49 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
50 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
51 import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
52 import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassResolverProcessor;
53 import org.jetbrains.plugins.groovy.lang.resolve.processors.MethodResolverProcessor;
54 import org.jetbrains.plugins.groovy.lang.resolve.processors.PropertyResolverProcessor;
55 import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor;
56
57 import java.util.*;
58 import java.util.concurrent.ConcurrentHashMap;
59
60 /**
61  * @author ven
62  */
63 @SuppressWarnings({"StringBufferReplaceableByString"})
64 public class ResolveUtil {
65   public static final PsiScopeProcessor.Event DECLARATION_SCOPE_PASSED = new PsiScopeProcessor.Event() {};
66
67   private ResolveUtil() {
68   }
69
70   public static boolean treeWalkUp(@NotNull GroovyPsiElement place, PsiScopeProcessor processor, boolean processNonCodeMethods) {
71     PsiElement lastParent = null;
72     PsiElement run = place;
73
74     final Project project = place.getProject();
75     PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
76
77     while (run != null) {
78       if (!run.processDeclarations(processor, ResolveState.initial(), lastParent, place)) return false;
79       if (processNonCodeMethods) {
80         if (run instanceof GrTypeDefinition) {
81           processNonCodeMethods(factory.createType(((GrTypeDefinition)run)), processor, place);
82         }
83         else if ((run instanceof GroovyFileBase) && ((GroovyFileBase)run).isScript()) {
84           final PsiClass psiClass = ((GroovyFileBase)run).getScriptClass();
85           if (psiClass != null) {
86             processNonCodeMethods(factory.createType(psiClass), processor, place);
87           }
88         }
89       }
90       lastParent = run;
91       run = run.getContext();
92       processor.handleEvent(JavaScopeProcessorEvent.CHANGE_LEVEL, null);
93     }
94
95     return true;
96   }
97
98   public static boolean processChildren(PsiElement element,
99                                         PsiScopeProcessor processor,
100                                         ResolveState substitutor,
101                                         PsiElement lastParent,
102                                         PsiElement place) {
103     PsiElement run = lastParent == null ? element.getLastChild() : lastParent.getPrevSibling();
104     while (run != null) {
105       if (!run.processDeclarations(processor, substitutor, null, place)) return false;
106       run = run.getPrevSibling();
107     }
108
109     return true;
110   }
111
112   @Nullable
113   public static String getNameHint(PsiScopeProcessor processor) {
114     NameHint nameHint = processor.getHint(NameHint.KEY);
115     if (nameHint == null) {
116       return null;
117     }
118
119     return nameHint.getName(ResolveState.initial());
120   }
121
122   public static boolean processElement(PsiScopeProcessor processor, PsiNamedElement namedElement, ResolveState state) {
123     NameHint nameHint = processor.getHint(NameHint.KEY);
124     //todo [DIANA] look more carefully
125     String name = nameHint == null ? null : nameHint.getName(state);
126     if (name == null || name.equals(namedElement.getName())) {
127       return processor.execute(namedElement, state);
128     }
129
130     return true;
131   }
132
133   public static boolean processAllDeclarations(@NotNull PsiType type,
134                                                @NotNull PsiScopeProcessor processor,
135                                                @NotNull ResolveState state,
136                                                @NotNull GroovyPsiElement place) {
137     if (type instanceof PsiClassType) {
138       final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
139       final PsiClass psiClass = resolveResult.getElement();
140       if (psiClass != null) {
141         if (!psiClass.processDeclarations(processor, state, null, place)) return false;
142       }
143     }
144     return processNonCodeMethods(type, processor, place, state);
145   }
146
147   public static boolean processNonCodeMethods(PsiType type,
148                                               PsiScopeProcessor processor,
149                                               GroovyPsiElement place) {
150     return processNonCodeMethods(type, processor, place, ResolveState.initial());
151   }
152
153   public static boolean processNonCodeMethods(PsiType type,
154                                               PsiScopeProcessor processor,
155                                               GroovyPsiElement place,
156                                               ResolveState state) {
157     if (!NonCodeMembersContributor.runContributors(type, processor, place, state)) {
158       return false;
159     }
160
161     if (!GroovyDslFileIndex.processExecutors(type, place, processor, state)) {
162       return false;
163     }
164
165     return true;
166   }
167
168   private static final Key<PsiType> COMPARABLE = Key.create(CommonClassNames.JAVA_LANG_COMPARABLE);
169   private static final Key<PsiType> SERIALIZABLE = Key.create(CommonClassNames.JAVA_IO_SERIALIZABLE);
170   private static final Key<PsiType> STRING = Key.create(CommonClassNames.JAVA_LANG_STRING);
171
172   private static void collectSuperTypes(PsiType type, Map<String, PsiType> visited, Project project) {
173     String qName = rawCanonicalText(type);
174
175     if (visited.put(qName, type) != null) {
176       return;
177     }
178
179     final PsiType[] superTypes = type.getSuperTypes();
180     for (PsiType superType : superTypes) {
181       collectSuperTypes(TypeConversionUtil.erasure(superType), visited, project);
182     }
183
184     if (type instanceof PsiArrayType && superTypes.length == 0) {
185       PsiType comparable = createTypeFromText(project, COMPARABLE, CommonClassNames.JAVA_LANG_COMPARABLE);
186       PsiType serializable = createTypeFromText(project, SERIALIZABLE, CommonClassNames.JAVA_IO_SERIALIZABLE);
187       collectSuperTypes(comparable, visited, project);
188       collectSuperTypes(serializable, visited, project);
189     }
190
191     if (GroovyCommonClassNames.GROOVY_LANG_GSTRING.equals(qName)) {
192       collectSuperTypes(createTypeFromText(project, STRING, CommonClassNames.JAVA_LANG_STRING), visited, project);
193     }
194
195   }
196
197   public static PsiType createTypeFromText(Project project, Key<PsiType> key, String text) {
198     final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
199     PsiType type = project.getUserData(key);
200     if (type == null) {
201       type = factory.createTypeFromText(text, null);
202       project.putUserData(key, type);
203     }
204     return type;
205   }
206
207   public static Map<String, PsiType> getAllSuperTypes(@NotNull PsiType base, final Project project) {
208     final Map<String, Map<String, PsiType>> cache =
209       CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider<Map<String, Map<String, PsiType>>>() {
210         @Override
211         public Result<Map<String, Map<String, PsiType>>> compute() {
212           final Map<String, Map<String, PsiType>> result = new ConcurrentHashMap<String, Map<String, PsiType>>();
213           return Result.create(result, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(project));
214         }
215       });
216
217     final PsiClass cls = PsiUtil.resolveClassInType(base);
218     //noinspection ConstantConditions
219     String key;
220     if (cls instanceof PsiTypeParameter) {
221       final PsiClass superClass = cls.getSuperClass();
222       key = cls.getName() + (superClass == null ? CommonClassNames.JAVA_LANG_OBJECT : superClass.getName());
223     }
224     else {
225       key = TypeConversionUtil.erasure(base).getCanonicalText();
226     }
227     if (key == null) key = "";
228     Map<String, PsiType> result = cache.get(key);
229     if (result == null) {
230       result = new HashMap<String, PsiType>();
231       collectSuperTypes(base, result, project);
232       cache.put(key, result);
233     }
234     return result;
235   }
236
237   public static boolean isInheritor(PsiType type, @NotNull String baseClass, Project project) {
238     return getAllSuperTypes(type, project).keySet().contains(baseClass);
239   }
240
241
242   @NotNull
243   private static String rawCanonicalText(@NotNull PsiType type) {
244     final String result = type.getCanonicalText();
245     if (result == null) return "";
246     final int i = result.indexOf('<');
247     if (i > 0) return result.substring(0, i);
248     return result;
249   }
250
251   @Nullable
252   public static PsiType getListTypeForSpreadOperator(GrReferenceExpression refExpr, PsiType componentType) {
253     PsiClass clazz = JavaPsiFacade.getInstance(refExpr.getManager().getProject())
254       .findClass(CommonClassNames.JAVA_UTIL_LIST, refExpr.getResolveScope());
255     if (clazz != null) {
256       PsiTypeParameter[] typeParameters = clazz.getTypeParameters();
257       if (typeParameters.length == 1) {
258         PsiSubstitutor substitutor = PsiSubstitutor.EMPTY.put(typeParameters[0], componentType);
259         return JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory().createType(clazz, substitutor);
260       }
261     }
262
263     return null;
264   }
265
266   public static GroovyPsiElement resolveProperty(GroovyPsiElement place, String name) {
267     PropertyResolverProcessor processor = new PropertyResolverProcessor(name, place);
268     return resolveExistingElement(place, processor, GrVariable.class, GrReferenceExpression.class);
269   }
270
271   @Nullable
272   public static PsiClass resolveClass(GroovyPsiElement place, String name) {
273     ClassResolverProcessor processor = new ClassResolverProcessor(name, place);
274     return resolveExistingElement(place, processor, PsiClass.class);
275   }
276
277   @Nullable
278   public static <T> T resolveExistingElement(GroovyPsiElement place, ResolverProcessor processor, Class<? extends T>... classes) {
279     treeWalkUp(place, processor, true);
280     final GroovyResolveResult[] candidates = processor.getCandidates();
281     for (GroovyResolveResult candidate : candidates) {
282       final PsiElement element = candidate.getElement();
283       if (element == place) continue;
284       for (Class<? extends T> clazz : classes) {
285         if (clazz.isInstance(element)) //noinspection unchecked
286           return (T)element;
287       }
288     }
289
290     return null;
291   }
292
293   @NotNull
294   public static Pair<GrStatement, GrLabeledStatement> resolveLabelTargets(@Nullable String labelName,
295                                                                           @Nullable PsiElement element,
296                                                                           boolean isBreak) {
297     if (element == null) return new Pair<GrStatement, GrLabeledStatement>(null, null);
298
299     if (labelName == null) {
300       do {
301         element = element.getParent();
302         if (element == null || element instanceof GrClosableBlock || element instanceof GrMember || element instanceof GroovyFile) {
303           return new Pair<GrStatement, GrLabeledStatement>(null, null);
304         }
305       }
306       while (!(element instanceof GrLoopStatement) && !(isBreak && element instanceof GrSwitchStatement));
307       return new Pair<GrStatement, GrLabeledStatement>(((GrStatement)element), null);
308     }
309
310     GrStatement statement = null;
311     do {
312       PsiElement last = element;
313       element = element.getParent();
314       if (element == null || element instanceof GrMember || element instanceof GroovyFile) break;
315       if (element instanceof GrStatement && !(element instanceof GrClosableBlock)) {
316         statement = (GrStatement)element;
317       }
318       PsiElement sibling = element;
319       while (sibling != null) {
320         final GrLabeledStatement labelStatement = findLabelStatementIn(sibling, last, labelName);
321         if (labelStatement != null) {
322           return new Pair<GrStatement, GrLabeledStatement>(statement, labelStatement);
323         }
324         sibling = sibling.getPrevSibling();
325       }
326       if (element instanceof GrClosableBlock) break;
327     }
328     while (true);
329     return new Pair<GrStatement, GrLabeledStatement>(null, null);
330   }
331
332   private static boolean isApplicableLabelStatement(PsiElement element, String labelName) {
333     return ((element instanceof GrLabeledStatement && labelName.equals(((GrLabeledStatement)element).getLabelName())));
334   }
335
336   @Nullable
337   private static GrLabeledStatement findLabelStatementIn(PsiElement element, PsiElement lastChild, String labelName) {
338     if (isApplicableLabelStatement(element, labelName)) {
339       return (GrLabeledStatement)element;
340     }
341     for (PsiElement child = element.getFirstChild(); child != null && child != lastChild; child = child.getNextSibling()) {
342       final GrLabeledStatement statement = findLabelStatementIn(child, child, labelName);
343       if (statement != null) return statement;
344     }
345     return null;
346   }
347
348   @Nullable
349   public static GrLabeledStatement resolveLabeledStatement(@Nullable String labelName, @Nullable PsiElement element, boolean isBreak) {
350     return resolveLabelTargets(labelName, element, isBreak).second;
351   }
352
353   @Nullable
354   public static GrStatement resolveLabelTargetStatement(@Nullable String labelName, @Nullable PsiElement element, boolean isBreak) {
355     return resolveLabelTargets(labelName, element, isBreak).first;
356   }
357
358   public static boolean processCategoryMembers(PsiElement place, ResolverProcessor processor) {
359     PsiElement prev = null;
360     Ref<Boolean> result = new Ref<Boolean>(null);
361     while (place != null) {
362       if (place instanceof GrMember) break;
363       if (categoryIteration(place, processor, prev, result)) return result.get();
364
365       prev = place;
366       place = place.getContext();
367     }
368
369     return true;
370   }
371
372   private static boolean categoryIteration(PsiElement place, ResolverProcessor processor, PsiElement prev, Ref<Boolean> result) {
373     if (!(place instanceof GrMethodCallExpression)) return false;
374
375     final GrMethodCallExpression call = (GrMethodCallExpression)place;
376     final GrExpression invoked = call.getInvokedExpression();
377     if (!(invoked instanceof GrReferenceExpression) || !"use".equals(((GrReferenceExpression)invoked).getReferenceName())) return false;
378
379     final GrClosableBlock[] closures = call.getClosureArguments();
380     if (closures.length != 1 || !closures[0].equals(prev)) return false;
381
382     if (!useCategoryClass(call)) return false;
383
384     final GrArgumentList argList = call.getArgumentList();
385     if (argList == null) return false;
386
387     result.set(Boolean.TRUE);
388     final GrExpression[] args = argList.getExpressionArguments();
389     for (GrExpression arg : args) {
390       if (arg instanceof GrReferenceExpression) {
391         final PsiElement resolved = ((GrReferenceExpression)arg).resolve();
392         if (resolved instanceof PsiClass) {
393           if (!resolved.processDeclarations(processor, ResolveState.initial().put(ResolverProcessor.RESOLVE_CONTEXT, call), null, place)) {
394             result.set(Boolean.FALSE);
395           }
396         }
397       }
398     }
399     return true;
400   }
401
402   private static boolean useCategoryClass(GrMethodCallExpression call) {
403     return call.resolveMethod() instanceof GrGdkMethod;
404   }
405
406   public static PsiElement[] mapToElements(GroovyResolveResult[] candidates) {
407     PsiElement[] elements = new PsiElement[candidates.length];
408     for (int i = 0; i < elements.length; i++) {
409       elements[i] = candidates[i].getElement();
410     }
411
412     return elements;
413   }
414
415   public static GroovyResolveResult[] filterSameSignatureCandidates(Collection<GroovyResolveResult> candidates, int argumentCount) {
416     GroovyResolveResult[] array = candidates.toArray(new GroovyResolveResult[candidates.size()]);
417     if (array.length == 1) return array;
418
419     List<GroovyResolveResult> result = new ArrayList<GroovyResolveResult>();
420     result.add(array[0]);
421
422     Outer:
423     for (int i = 1; i < array.length; i++) {
424       PsiElement currentElement = array[i].getElement();
425       if (currentElement instanceof PsiMethod) {
426         PsiMethod currentMethod = (PsiMethod)currentElement;
427         for (Iterator<GroovyResolveResult> iterator = result.iterator(); iterator.hasNext();) {
428           final GroovyResolveResult otherResolveResult = iterator.next();
429           PsiElement element = otherResolveResult.getElement();
430           if (element instanceof PsiMethod) {
431             PsiMethod method = (PsiMethod)element;
432             if (dominated(currentMethod, array[i].getSubstitutor(), method, otherResolveResult.getSubstitutor())) {
433               continue Outer;
434             }
435             else if (dominated(method, otherResolveResult.getSubstitutor(), currentMethod, array[i].getSubstitutor())) {
436               iterator.remove();
437             }
438           }
439         }
440       }
441
442       result.add(array[i]);
443     }
444
445     return result.toArray(new GroovyResolveResult[result.size()]);
446   }
447
448   public static boolean dominated(PsiMethod method1,
449                                   PsiSubstitutor substitutor1,
450                                   PsiMethod method2,
451                                   PsiSubstitutor substitutor2) {  //method1 has more general parameter types then method2
452     if (!method1.getName().equals(method2.getName())) return false;
453
454     PsiParameter[] params1 = method1.getParameterList().getParameters();
455     PsiParameter[] params2 = method2.getParameterList().getParameters();
456
457     if (params1.length != params2.length) return false;
458
459     for (int i = 0; i < params2.length; i++) {
460       PsiType type1 = substitutor1.substitute(params1[i].getType());
461       PsiType type2 = substitutor2.substitute(params2[i].getType());
462       if (!type1.equals(type2)) return false;
463     }
464
465     return true;
466   }
467
468   public static GroovyResolveResult[] getCallVariants(GroovyPsiElement place) {
469     final PsiElement parent = place.getParent();
470     GroovyResolveResult[] variants = GroovyResolveResult.EMPTY_ARRAY;
471     if (parent instanceof GrCallExpression) {
472       variants = ((GrCallExpression)parent).getCallVariants(place instanceof GrExpression ? (GrExpression)place : null);
473     }
474     else if (parent instanceof GrConstructorInvocation) {
475       final PsiClass clazz = ((GrConstructorInvocation)parent).getDelegatedClass();
476       if (clazz != null) {
477         final PsiMethod[] constructors = clazz.getConstructors();
478         variants = getConstructorResolveResult(constructors, place);
479       }
480     }
481     else if (parent instanceof GrAnonymousClassDefinition) {
482       final PsiElement element = ((GrAnonymousClassDefinition)parent).getBaseClassReferenceGroovy().resolve();
483       if (element instanceof PsiClass) {
484         final PsiMethod[] constructors = ((PsiClass)element).getConstructors();
485         variants = getConstructorResolveResult(constructors, place);
486       }
487     }
488     else if (place instanceof GrReferenceExpression) {
489       variants = ((GrReferenceExpression)place).getSameNameVariants();
490     }
491     return variants;
492   }
493
494   public static GroovyResolveResult[] getConstructorResolveResult(PsiMethod[] constructors, PsiElement place) {
495     GroovyResolveResult[] variants = new GroovyResolveResult[constructors.length];
496     for (int i = 0; i < constructors.length; i++) {
497       final boolean isAccessible = PsiUtil.isAccessible(constructors[i], place, null);
498       variants[i] = new GroovyResolveResultImpl(constructors[i], isAccessible);
499     }
500     return variants;
501   }
502
503   public static GroovyResolveResult[] getNonCodeConstructors(PsiClass psiClass, GroovyPsiElement place, PsiSubstitutor substitutor) {
504     final PsiClassType qualifierType = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass);
505     final MethodResolverProcessor processor = new MethodResolverProcessor(psiClass.getName(), place, true, null, null, PsiType.EMPTY_ARRAY);
506     NonCodeMembersContributor
507       .runContributors(qualifierType, processor, place, ResolveState.initial().put(PsiSubstitutor.KEY, substitutor));
508     return processor.getCandidates();
509   }
510
511   public static PsiMethod[] getAllClassConstructors(PsiClass psiClass, GroovyPsiElement place, PsiSubstitutor substitutor) {
512     final PsiMethod[] realConstructors = psiClass.getConstructors();
513     final GroovyResolveResult[] nonCodeConstructors = getNonCodeConstructors(psiClass, place, substitutor);
514     PsiMethod[] constructors = new PsiMethod[realConstructors.length + nonCodeConstructors.length];
515     System.arraycopy(realConstructors, 0, constructors, 0, realConstructors.length);
516     for (int i = 0; i < nonCodeConstructors.length; i++) {
517       GroovyResolveResult nonCodeConstructor = nonCodeConstructors[i];
518       final PsiElement element = nonCodeConstructor.getElement();
519       if (element instanceof PsiMethod) {
520         constructors[i + realConstructors.length] = (PsiMethod)element;
521       }
522     }
523     return constructors;
524   }
525
526   public static boolean isInUseScope(GroovyResolveResult resolveResult) {
527     return resolveResult != null && isInUseScope(resolveResult.getCurrentFileResolveContext());
528   }
529
530   public static boolean isInUseScope(PsiElement context) {
531     if (context instanceof GrMethodCall) {
532       final GrExpression expression = ((GrMethodCall)context).getInvokedExpression();
533       if (expression instanceof GrReferenceExpression) {
534         final PsiElement resolved = ((GrReferenceExpression)expression).resolve();
535         if (resolved instanceof GrGdkMethod && "use".equals(((GrGdkMethod)resolved).getStaticMethod().getName())) {
536           return true;
537         }
538       }
539     }
540     return false;
541   }
542
543   public static boolean isInWithContext(GroovyResolveResult resolveResult) {
544     return isInWithContext(resolveResult.getCurrentFileResolveContext());
545   }
546
547   public static boolean isInWithContext(GroovyPsiElement resolveContext) {
548     if (resolveContext instanceof GrExpression) {
549       final PsiElement parent = resolveContext.getParent();
550       if (parent instanceof GrReferenceExpression && ((GrReferenceExpression)parent).getQualifier() == resolveContext) {
551         final PsiElement pparent = parent.getParent();
552         if (pparent instanceof GrMethodCall) {
553           final PsiMethod method = ((GrMethodCall)pparent).resolveMethod();
554           if (method instanceof GrGdkMethod && "with".equals(method.getName())) {
555             return true;
556           }
557         }
558       }
559     }
560     return false;
561   }
562
563   public static boolean referenceIsKeyOfMap(GrReferenceExpression ref) {
564     final GrExpression qualifier = ref.getQualifierExpression();
565     if (qualifier != null) {
566       return InheritanceUtil.isInheritor(qualifier.getType(), CommonClassNames.JAVA_UTIL_MAP);
567     }
568
569     PsiElement place = ref;
570     while (true) {
571       final GrClosableBlock closure =
572         PsiTreeUtil.getParentOfType(place, GrClosableBlock.class, true, GrMethod.class, GroovyFile.class, GrTypeDefinitionBody.class);
573       if (closure == null) break;
574       place = closure;
575       PsiElement clParent = closure.getParent();
576       if (clParent instanceof GrArgumentList) clParent = clParent.getParent();
577       if (!(clParent instanceof GrMethodCall)) continue;
578       final GrExpression expression = ((GrMethodCall)clParent).getInvokedExpression();
579       if (expression instanceof GrReferenceExpression &&
580           "with".equals(((GrReferenceExpression)expression).getReferenceName()) &&
581           ((GrReferenceExpression)expression).resolve() instanceof GrGdkMethod) {
582         final GrExpression withQualifier = ((GrReferenceExpression)expression).getQualifierExpression();
583         if (withQualifier != null) {
584           return InheritanceUtil.isInheritor(withQualifier.getType(), CommonClassNames.JAVA_UTIL_MAP);
585         }
586       }
587     }
588     return false;
589   }
590
591   @NotNull
592   public static GroovyResolveResult[] getMethodCandidates(@NotNull PsiType thisType,
593                                                           @Nullable String methodName,
594                                                           @NotNull GroovyPsiElement place,
595                                                           @Nullable PsiType[] argumentTypes) {
596     if (methodName != null) {
597       MethodResolverProcessor processor =
598         new MethodResolverProcessor(methodName, place, false, thisType, argumentTypes, PsiType.EMPTY_ARRAY);
599       final ResolveState state;
600       if (thisType instanceof PsiClassType) {
601         final PsiClassType classtype = (PsiClassType)thisType;
602         final PsiClassType.ClassResolveResult resolveResult = classtype.resolveGenerics();
603         final PsiClass lClass = resolveResult.getElement();
604         state = ResolveState.initial().put(PsiSubstitutor.KEY, resolveResult.getSubstitutor());
605         if (lClass != null) {
606           lClass.processDeclarations(processor, state, null, place);
607         }
608       }
609       else {
610         state = ResolveState.initial();
611       }
612
613       processNonCodeMethods(thisType, processor, place, state);
614       processCategoryMembers(place, processor);
615       return processor.getCandidates();
616     }
617     return GroovyResolveResult.EMPTY_ARRAY;
618   }
619 }