2 * Copyright 2000-2015 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package org.jetbrains.plugins.groovy.lang.resolve;
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.*;
79 public class ResolveUtil {
80 private static final Logger LOG = Logger.getInstance(ResolveUtil.class);
82 public static final PsiScopeProcessor.Event DECLARATION_SCOPE_PASSED = new PsiScopeProcessor.Event() {};
84 private ResolveUtil() {
89 * @param place - place to start tree walk up
91 * @param processNonCodeMethods - this parameter tells us if we need non code members
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());
102 * @param place - place to start tree walk up
104 * @param processNonCodeMethods - this parameter tells us if we need non code members
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) {
114 ClassHint hint = processor.getHint(ClassHint.KEY);
116 return new DeclarationCacheKey(getNameHint(processor), hint, processNonCodeMethods, originalPlace).processCachedDeclarations(place, processor);
119 final PsiScopeProcessor nonCodeProcessor = processNonCodeMethods ? processor : null;
120 return doTreeWalkUp(place, originalPlace, processor, nonCodeProcessor, state);
122 catch (StackOverflowError e) {
123 LOG.error("StackOverflow", e, place.getContainingFile().getText());
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;
135 return PsiTreeUtil.treeWalkUp(place, maxScope, new PairProcessor<PsiElement, PsiElement>() {
137 public boolean process(PsiElement scope, PsiElement lastParent) {
138 ProgressManager.checkCanceled();
139 if (!doProcessDeclarations(originalPlace, lastParent, scope, substituteProcessor(processor, scope), nonCodeProcessor, state)) {
142 issueLevelChangeEvents(processor, scope);
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;
158 if (!scope.processDeclarations(plainProcessor, state, lastParent, place)) return false;
160 if (scope instanceof GrTypeDefinition || scope instanceof GrClosableBlock) {
161 if (!processStaticImports(plainProcessor, place.getContainingFile(), state, place)) return false;
165 if (nonCodeProcessor != null) {
166 if (!processScopeNonCodeMembers(place, lastParent, nonCodeProcessor, scope, state)) return false;
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);
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);
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;
198 //@Category(CategoryType)
200 PsiClassType categoryType = GdkMethodUtil.getCategoryType((PsiClass)scope);
201 if (categoryType != null) {
202 if (!processNonCodeMembers(categoryType, processor, place, state)) return false;
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;
214 if (scope instanceof GrClosableBlock) {
215 ResolveState _state = state.put(ClassHint.RESOLVE_CONTEXT, scope);
217 PsiClass superClass = getLiteralSuperClass((GrClosableBlock)scope);
218 if (superClass != null && !superClass.processDeclarations(processor, _state, null, place)) return false;
220 if (!GdkMethodUtil.categoryIteration((GrClosableBlock)scope, processor, _state)) return false;
221 if (!processNonCodeMembers(GrClosureType.create(((GrClosableBlock)scope), false), processor, place, _state)) return false;
224 if (scope instanceof GrStatementOwner) {
225 if (!GdkMethodUtil.processMixinToMetaclass((GrStatementOwner)scope, processor, state, lastParent, place)) return false;
232 private static PsiClassType createPsiType(@NotNull PsiClass psiClass) {
233 PsiElementFactory factory = JavaPsiFacade.getElementFactory(psiClass.getProject());
234 return factory.createType(psiClass);
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;
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();
254 public static String getNameHint(PsiScopeProcessor processor) {
255 NameHint nameHint = processor.getHint(NameHint.KEY);
256 if (nameHint == null) {
260 return nameHint.getName(ResolveState.initial());
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);
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);
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;
297 if (!processCategoryMembers(place, nonCodeProcessor, state)) return false;
298 if (!processNonCodeMembers(type, nonCodeProcessor, place, state)) return false;
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();
309 if (!NonCodeMembersContributor.runContributors(type, processor, place, state)) {
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);
320 private static void collectSuperTypes(PsiType type, Set<String> visited, Project project) {
321 String qName = rawCanonicalText(type);
323 if (!visited.add(qName)) {
327 final PsiType[] superTypes = type.getSuperTypes();
328 for (PsiType superType : superTypes) {
329 collectSuperTypes(TypeConversionUtil.erasure(superType), visited, project);
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);
339 if (GroovyCommonClassNames.GROOVY_LANG_GSTRING.equals(qName)) {
340 collectSuperTypes(createTypeFromText(project, STRING, CommonClassNames.JAVA_LANG_STRING), visited, project);
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);
349 type = factory.createTypeFromText(text, null);
350 project.putUserData(key, type);
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>>>() {
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);
365 final PsiClass cls = PsiUtil.resolveClassInType(base);
367 if (cls instanceof PsiTypeParameter) {
368 final PsiClass superClass = cls.getSuperClass();
369 key = cls.getName() + (superClass == null ? CommonClassNames.JAVA_LANG_OBJECT : superClass.getName());
371 else if (base instanceof PsiClassType) {
372 key = TypesUtil.getQualifiedName(base);
375 key = base.getCanonicalText();
377 Set<String> result = key == null ? null : cache.get(key);
378 if (result == null) {
379 result = ContainerUtil.newHashSet();
380 collectSuperTypes(base, result, project);
382 cache.put(key, result);
389 private static String rawCanonicalText(@NotNull PsiType type) {
390 if (type instanceof PsiClassType) {
391 String qname = TypesUtil.getQualifiedName(type);
396 return TypeConversionUtil.erasure(type).getCanonicalText();
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);
405 public static PsiClass resolveClass(GroovyPsiElement place, String name) {
406 ClassResolverProcessor processor = new ClassResolverProcessor(name, place);
407 return resolveExistingElement(place, processor, PsiClass.class);
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
427 public static Pair<GrStatement, GrLabeledStatement> resolveLabelTargets(@Nullable String labelName,
428 @Nullable PsiElement element,
430 if (element == null) return new Pair<GrStatement, GrLabeledStatement>(null, null);
432 if (labelName == null) {
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);
439 while (!(element instanceof GrLoopStatement) && !(isBreak && element instanceof GrSwitchStatement));
440 return new Pair<GrStatement, GrLabeledStatement>(((GrStatement)element), null);
443 GrStatement statement = null;
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;
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);
457 sibling = sibling.getPrevSibling();
459 if (element instanceof GrClosableBlock) break;
462 return new Pair<GrStatement, GrLabeledStatement>(null, null);
465 private static boolean isApplicableLabelStatement(PsiElement element, String labelName) {
466 return ((element instanceof GrLabeledStatement && labelName.equals(((GrLabeledStatement)element).getName())));
470 private static GrLabeledStatement findLabelStatementIn(PsiElement element, PsiElement lastChild, String labelName) {
471 if (isApplicableLabelStatement(element, labelName)) {
472 return (GrLabeledStatement)element;
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;
482 public static GrLabeledStatement resolveLabeledStatement(@Nullable String labelName, @Nullable PsiElement element, boolean isBreak) {
483 return resolveLabelTargets(labelName, element, isBreak).second;
487 public static GrStatement resolveLabelTargetStatement(@Nullable String labelName, @Nullable PsiElement element, boolean isBreak) {
488 return resolveLabelTargets(labelName, element, isBreak).first;
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;
498 while (run != null) {
499 ProgressManager.checkCanceled();
500 if (run instanceof GrMember) {
503 if (run instanceof GrClosableBlock) {
505 if (!GdkMethodUtil.categoryIteration((GrClosableBlock)run, processor, state)) return false;
508 PsiClass superClass = getLiteralSuperClass((GrClosableBlock)run);
509 if (superClass != null && !GdkMethodUtil.processCategoryMethods(run, processor, state, superClass)) return false;
512 if (run instanceof GrStatementOwner) {
513 if (!GdkMethodUtil.processMixinToMetaclass((GrStatementOwner)run, processor, state, lastParent, place)) return false;
517 run = run.getContext();
524 private static PsiClass getLiteralSuperClass(GrClosableBlock closure) {
526 if (closure.getParent() instanceof GrNamedArgument && closure.getParent().getParent() instanceof GrListOrMap) {
527 type = LiteralConstructorReference.getTargetConversionType((GrListOrMap)closure.getParent().getParent());
530 type = LiteralConstructorReference.getTargetConversionType(closure);
532 return type != null ? type.resolve() : null;
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();
544 public static GroovyResolveResult[] filterSameSignatureCandidates(Collection<GroovyResolveResult> candidates) {
545 GroovyResolveResult[] array = candidates.toArray(new GroovyResolveResult[candidates.size()]);
546 if (array.length == 1) return array;
548 List<GroovyResolveResult> result = new ArrayList<GroovyResolveResult>();
549 result.add(array[0]);
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())) {
564 else if (dominated(method, otherResolveResult.getSubstitutor(), currentMethod, array[i].getSubstitutor())) {
571 result.add(array[i]);
574 return result.toArray(new GroovyResolveResult[result.size()]);
577 public static boolean dominated(PsiMethod method1,
578 PsiSubstitutor substitutor1,
580 PsiSubstitutor substitutor2) { //method1 has more general parameter types then method2
581 if (!method1.getName().equals(method2.getName())) return false;
583 PsiParameter[] params1 = method1.getParameterList().getParameters();
584 PsiParameter[] params2 = method2.getParameterList().getParameters();
586 if (params1.length != params2.length) return false;
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;
594 if (method1 instanceof GrGdkMethod && method2 instanceof GrGdkMethod) {
595 PsiMethod static1 = ((GrGdkMethod)method1).getStaticMethod();
596 PsiMethod static2 = ((GrGdkMethod)method2).getStaticMethod();
598 PsiParameter p1 = static1.getParameterList().getParameters()[0];
599 PsiParameter p2 = static2.getParameterList().getParameters()[0];
601 PsiType t1 = substitutor1.substitute(p1.getType());
602 PsiType t2 = substitutor2.substitute(p2.getType());
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);
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);
621 else if (parent instanceof GrConstructorInvocation) {
622 final PsiClass clazz = ((GrConstructorInvocation)parent).getDelegatedClass();
624 final PsiMethod[] constructors = clazz.getConstructors();
625 variants = getConstructorResolveResult(constructors, place);
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);
635 else if (place instanceof GrReferenceExpression) {
636 variants = ((GrReferenceExpression)place).getSameNameVariants();
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);
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);
660 final PsiClassType qualifierType = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass);
661 processNonCodeMembers(qualifierType, processor, place, state);
662 return processor.getCandidates();
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);
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);
680 public static GrExpression getSelfOrWithQualifier(GrReferenceExpression ref) {
681 final GrExpression qualifier = ref.getQualifierExpression();
682 if (qualifier != null) {
686 PsiElement place = ref;
688 final GrClosableBlock closure = PsiTreeUtil.getParentOfType(place, GrClosableBlock.class, true, GrMember.class, GroovyFile.class);
689 if (closure == null) break;
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;
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);
717 public static GroovyResolveResult[] getMethodCandidates(@NotNull PsiType thisType,
718 @Nullable String methodName,
719 @NotNull PsiElement place,
720 boolean resolveClosures,
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;
734 final GroovyResolveResult[] allPropertyCandidates;
735 if (resolveClosures) {
736 PropertyResolverProcessor propertyResolver = new PropertyResolverProcessor(methodName, place);
737 processAllDeclarations(thisType, propertyResolver, state, place);
738 allPropertyCandidates = propertyResolver.getCandidates();
741 allPropertyCandidates = GroovyResolveResult.EMPTY_ARRAY;
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);
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};
764 List<GroovyResolveResult> allCandidates = new ArrayList<GroovyResolveResult>();
765 if (hasApplicableMethods) {
766 ContainerUtil.addAll(allCandidates, methodCandidates);
768 ContainerUtil.addAll(allCandidates, propertyCandidates);
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);
785 if (applicable.size() == 1) {
786 return applicable.toArray(new GroovyResolveResult[applicable.size()]);
788 ContainerUtil.addAll(allCandidates, applicable);
791 if (!allCandidates.isEmpty()) {
792 return allCandidates.toArray(new GroovyResolveResult[allCandidates.size()]);
794 else if (!hasApplicableMethods) {
795 return methodCandidates;
797 return GroovyResolveResult.EMPTY_ARRAY;
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;
804 final GrSignature signature = ((GrClosureType)type).getSignature();
805 return GrClosureSignatureUtil.isSignatureApplicable(signature, argTypes, place);
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);
817 if (element instanceof GrField) {
818 type = ((GrField)element).getTypeGroovy();
820 else if (element instanceof PsiMethod) {
821 type = org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getSmartReturnType((PsiMethod)element);
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);
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;
839 PsiClass aClass = ((PsiEnumConstant)resolved).getContainingClass();
840 if (aClass == null) return false;
841 return qName.equals(aClass.getQualifiedName());
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;
853 public static PsiClass resolveAnnotation(PsiElement insideAnnotation) {
854 final GrAnnotation annotation = PsiTreeUtil.getParentOfType(insideAnnotation, GrAnnotation.class, false);
855 if (annotation == null) return null;
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;
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();
877 public static PsiNamedElement findDuplicate(@NotNull GrVariable variable) {
878 if (isScriptField(variable)) {
879 final String name = variable.getName();
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++;
888 return count > 1 ? GrScriptField.getScriptField(variable) : null;
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);
901 if (duplicate instanceof GrLightParameter && "args".equals(duplicate.getName())) {
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;
917 return qualifier == null;
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();
930 for (GroovyResolveResult variant : variants) {
931 final Map<GrExpression, Pair<PsiParameter, PsiType>> map = GrClosureSignatureUtil.mapArgumentsToParameters(
932 variant, place, true, true, namedArguments, expressionArguments, closureArguments
936 final Pair<PsiParameter, PsiType> pair = map.get(arg);
937 ContainerUtil.addIfNotNull(expectedParams, pair);
940 return expectedParams;
943 public static boolean shouldProcessClasses(ClassHint classHint) {
944 return classHint == null || classHint.shouldProcess(ClassHint.ResolveKind.CLASS);
947 public static boolean shouldProcessMethods(ClassHint classHint) {
948 return classHint == null || classHint.shouldProcess(ClassHint.ResolveKind.METHOD);
951 public static boolean shouldProcessProperties(ClassHint classHint) {
952 return classHint == null || classHint.shouldProcess(ClassHint.ResolveKind.PROPERTY);
955 public static boolean shouldProcessPackages(ClassHint classHint) {
956 return classHint == null || classHint.shouldProcess(ClassHint.ResolveKind.PACKAGE);
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;
965 return file.processDeclarations(new GrDelegatingScopeProcessorWithHints(resolver, null, ClassHint.RESOLVE_KINDS_METHOD) {
967 public boolean execute(@NotNull PsiElement element, @NotNull ResolveState _state) {
968 if (_state.get(RESOLVE_CONTEXT) instanceof GrImportStatement) {
969 super.execute(element, _state);
973 }, state, null, place);
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);
984 private static class DuplicateVariablesProcessor extends PropertyResolverProcessor {
985 private boolean myBorderPassed;
986 private final boolean myHasVisibilityModifier;
988 public DuplicateVariablesProcessor(GrVariable variable) {
989 super(variable.getName(), variable);
990 myBorderPassed = false;
991 myHasVisibilityModifier = hasExplicitVisibilityModifiers(variable);
994 private static boolean hasExplicitVisibilityModifiers(GrVariable variable) {
995 final GrModifierList modifierList = variable.getModifierList();
996 return modifierList != null && modifierList.hasExplicitVisibilityModifiers();
1000 public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
1001 if (myBorderPassed) {
1004 if (element instanceof GrVariable && hasExplicitVisibilityModifiers((GrVariable)element) != myHasVisibilityModifier) {
1007 if (element instanceof GrBindingVariable) return true;
1008 return super.execute(element, state);
1012 public void handleEvent(@NotNull Event event, Object associated) {
1013 if (event == DECLARATION_SCOPE_PASSED) {
1014 myBorderPassed = true;
1016 super.handleEvent(event, associated);