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