[groovy] make GrTraitType extend PsiIntersectionType. Tests included.
[idea/community.git] / plugins / groovy / groovy-psi / src / org / jetbrains / plugins / groovy / lang / psi / impl / statements / expressions / GrReferenceResolveRunner.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions;
17
18 import com.intellij.openapi.progress.ProgressManager;
19 import com.intellij.psi.*;
20 import com.intellij.psi.util.InheritanceUtil;
21 import org.jetbrains.annotations.NotNull;
22 import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
23 import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState;
24 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
25 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
26 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
27 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
28 import org.jetbrains.plugins.groovy.lang.psi.impl.GrTraitType;
29 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
30 import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
31 import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.ClosureParameterEnhancer;
32 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
33 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
34 import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint;
35 import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor;
36
37 /**
38  * @author Medvedev Max
39  */
40 public class GrReferenceResolveRunner {
41
42   private final GrReferenceExpression place;
43   private ResolverProcessor processor;
44
45   public GrReferenceResolveRunner(@NotNull GrReferenceExpression _place) {
46     place = _place;
47   }
48
49   public boolean resolveImpl(@NotNull ResolverProcessor _processor) {
50     processor = _processor;
51     try {
52       boolean result = doResolve();
53       ProgressManager.checkCanceled();
54       return result;
55     }
56     finally {
57       processor = null;
58     }
59   }
60
61   private boolean doResolve() {
62     GrExpression qualifier = place.getQualifier();
63     if (qualifier == null) {
64       if (!ResolveUtil.treeWalkUp(place, processor, true)) return false;
65       if (!processor.hasCandidates()) {
66         GrExpression runtimeQualifier = PsiImplUtil.getRuntimeQualifier(place);
67         if (runtimeQualifier != null) {
68           if (!processQualifier(runtimeQualifier)) return false;
69         }
70       }
71     }
72     else {
73       if (place.getDotTokenType() == GroovyTokenTypes.mSPREAD_DOT) {
74         final PsiType qtype = qualifier.getType();
75         final PsiType componentType = ClosureParameterEnhancer.findTypeForIteration(qtype, place);
76         if (componentType != null) {
77           final ResolveState state = ResolveState.initial()
78             .put(ClassHint.RESOLVE_CONTEXT, qualifier)
79             .put(SpreadState.SPREAD_STATE, SpreadState.create(qtype, null));
80           if (!processQualifierType(componentType, state)) return false;
81         }
82       }
83       else {
84         if (ResolveUtil.isClassReference(place)) return true;
85         if (!processQualifier(qualifier)) return false;
86         if (!processJavaLangClass(qualifier)) return false;
87       }
88     }
89     return true;
90   }
91
92   private boolean processJavaLangClass(@NotNull GrExpression qualifier) {
93     if (!(qualifier instanceof GrReferenceExpression)) return true;
94
95     //optimization: only 'class' or 'this' in static context can be an alias of java.lang.Class
96     if (!("class".equals(((GrReferenceExpression)qualifier).getReferenceName()) ||
97           PsiUtil.isThisReference(qualifier))) {
98       return true;
99     }
100
101     PsiType type = qualifier.getType();
102     if (!(type instanceof PsiClassType)) return true;
103
104     final PsiClass psiClass = ((PsiClassType)type).resolve();
105     if (psiClass == null || !CommonClassNames.JAVA_LANG_CLASS.equals(psiClass.getQualifiedName())) return true;
106
107     final PsiType[] params = ((PsiClassType)type).getParameters();
108     if (params.length != 1) return true;
109
110     if (!processQualifierType(params[0], ResolveState.initial().put(ClassHint.RESOLVE_CONTEXT, qualifier))) {
111       return false;
112     }
113     return true;
114   }
115
116   private boolean processQualifier(@NotNull GrExpression qualifier) {
117     PsiType qualifierType = qualifier.getType();
118     ResolveState state = ResolveState.initial().put(ClassHint.RESOLVE_CONTEXT, qualifier);
119     if (qualifierType == null || PsiType.VOID.equals(qualifierType)) {
120       if (qualifier instanceof GrReferenceExpression) {
121         PsiElement resolved = ((GrReferenceExpression)qualifier).resolve();
122         if (resolved != null && !resolved.processDeclarations(processor, state, null, place)) return false;
123         if (!(resolved instanceof PsiPackage)) {
124           PsiType objectQualifier = TypesUtil.getJavaLangObject(place);
125           if (!processQualifierType(objectQualifier, state)) return false;
126         }
127       }
128     }
129     else {
130       if (!processQualifierType(qualifierType, state)) return false;
131       if (qualifier instanceof GrReferenceExpression && !PsiUtil.isSuperReference(qualifier) && !PsiUtil.isInstanceThisRef(qualifier)) {
132         PsiElement resolved = ((GrReferenceExpression)qualifier).resolve();
133         if (resolved instanceof PsiClass) {
134           if (!processJavaLangClass(qualifierType, state)) return false;
135         }
136       }
137     }
138     return true;
139   }
140
141   private boolean processJavaLangClass(@NotNull PsiType qualifierType,
142                                        @NotNull ResolveState state) {
143     //omitted .class
144     PsiClass javaLangClass = PsiUtil.getJavaLangClass(place, place.getResolveScope());
145     if (javaLangClass == null) return true;
146
147     PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters();
148     PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
149     if (substitutor == null) substitutor = PsiSubstitutor.EMPTY;
150     if (typeParameters.length == 1) {
151       substitutor = substitutor.put(typeParameters[0], qualifierType);
152       state = state.put(PsiSubstitutor.KEY, substitutor);
153     }
154     if (!javaLangClass.processDeclarations(processor, state, null, place)) return false;
155
156     PsiType javaLangClassType = JavaPsiFacade.getElementFactory(place.getProject()).createType(javaLangClass, substitutor);
157
158     if (!ResolveUtil.processNonCodeMembers(javaLangClassType, processor, place, state)) return false;
159
160     return true;
161   }
162
163   private boolean processQualifierType(@NotNull PsiType originalQualifierType,
164                                        @NotNull ResolveState state) {
165     PsiType qualifierType = originalQualifierType instanceof PsiDisjunctionType
166                             ? ((PsiDisjunctionType)originalQualifierType).getLeastUpperBound()
167                             : originalQualifierType;
168
169     if (qualifierType instanceof GrTraitType) {
170       return processTraitType((GrTraitType)qualifierType, state);
171     }
172     else if (qualifierType instanceof PsiIntersectionType) {
173       for (PsiType conjunct : ((PsiIntersectionType)qualifierType).getConjuncts()) {
174         if (!processQualifierType(conjunct, state)) return false;
175       }
176       return true;
177     }
178
179     if (qualifierType instanceof PsiClassType) {
180       PsiClassType.ClassResolveResult qualifierResult = ((PsiClassType)qualifierType).resolveGenerics();
181       PsiClass qualifierClass = qualifierResult.getElement();
182       if (qualifierClass != null) {
183         if (!qualifierClass.processDeclarations(processor, state.put(PsiSubstitutor.KEY, qualifierResult.getSubstitutor()), null, place)) {
184           return false;
185         }
186       }
187     }
188     else if (qualifierType instanceof PsiArrayType) {
189       final GroovyPsiManager gmanager = GroovyPsiManager.getInstance(place.getProject());
190       final GrTypeDefinition arrayClass = gmanager.getArrayClass(((PsiArrayType)qualifierType).getComponentType());
191       if (arrayClass != null && !arrayClass.processDeclarations(processor, state, null, place)) return false;
192     }
193
194     if (!(place.getParent() instanceof GrMethodCall) && InheritanceUtil.isInheritor(qualifierType, CommonClassNames.JAVA_UTIL_COLLECTION)) {
195       final PsiType componentType = ClosureParameterEnhancer.findTypeForIteration(qualifierType, place);
196       if (componentType != null) {
197         final SpreadState spreadState = state.get(SpreadState.SPREAD_STATE);
198         processQualifierType(componentType, state.put(SpreadState.SPREAD_STATE, SpreadState.create(qualifierType, spreadState)));
199       }
200     }
201
202     if (!ResolveUtil.processCategoryMembers(place, processor, state)) return false;
203     if (!ResolveUtil.processNonCodeMembers(qualifierType, processor, place, state)) return false;
204     return true;
205   }
206
207   private boolean processTraitType(@NotNull GrTraitType traitType, @NotNull ResolveState state) {
208     final PsiType[] conjuncts = traitType.getConjuncts();
209     for (int i = conjuncts.length - 1; i >= 0; i--) {
210       if (!processQualifierType(conjuncts[i], state)) return false;
211     }
212     return true;
213   }
214 }