2 * Copyright 2000-2009 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.processors;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.pom.java.LanguageLevel;
21 import com.intellij.psi.*;
22 import com.intellij.psi.scope.JavaScopeProcessorEvent;
23 import com.intellij.psi.search.GlobalSearchScope;
24 import com.intellij.psi.util.PsiTreeUtil;
25 import com.intellij.psi.util.TypeConversionUtil;
26 import com.intellij.util.containers.hash.HashSet;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
29 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
30 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
31 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
32 import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement;
33 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression;
34 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
35 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
36 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
37 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureParameter;
38 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature;
39 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
40 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
41 import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil;
42 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
43 import org.jetbrains.plugins.groovy.lang.resolve.DominanceAwareMethod;
44 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
51 public class MethodResolverProcessor extends ResolverProcessor {
52 private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.lang.resolve.processors.MethodResolverProcessor");
53 private final PsiType myThisType;
55 private final PsiType[] myArgumentTypes;
56 private final PsiType[] myTypeArguments;
57 private final boolean myAllVariants;
59 private final Set<GroovyResolveResult> myInapplicableCandidates = new LinkedHashSet<GroovyResolveResult>();
60 private final boolean myIsConstructor;
62 private boolean myStopExecuting = false;
64 public MethodResolverProcessor(String name, GroovyPsiElement place, boolean isConstructor, PsiType thisType, @Nullable PsiType[] argumentTypes, PsiType[] typeArguments) {
65 this(name, place, isConstructor, thisType, argumentTypes, typeArguments, false);
67 public MethodResolverProcessor(String name, GroovyPsiElement place, boolean isConstructor, PsiType thisType, @Nullable PsiType[] argumentTypes, PsiType[] typeArguments, boolean allVariants) {
68 super(name, RESOLVE_KINDS_METHOD_PROPERTY, place, PsiType.EMPTY_ARRAY);
69 myIsConstructor = isConstructor;
70 myThisType = thisType;
71 myArgumentTypes = argumentTypes;
72 myTypeArguments = typeArguments;
73 myAllVariants = allVariants;
76 public boolean execute(PsiElement element, ResolveState state) {
77 if (myStopExecuting) {
80 PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
81 if (element instanceof PsiMethod) {
82 PsiMethod method = (PsiMethod) element;
84 if (method.isConstructor() != myIsConstructor) return true;
85 if (substitutor == null) substitutor = PsiSubstitutor.EMPTY;
86 substitutor = obtainSubstitutor(substitutor, method, state);
87 boolean isAccessible = isAccessible(method);
88 GroovyPsiElement resolveContext = state.get(RESOLVE_CONTEXT);
89 boolean isStaticsOK = isStaticsOK(method, resolveContext);
91 PsiUtil.isApplicable(myArgumentTypes, method, substitutor, ResolveUtil.isInUseScope(resolveContext), (GroovyPsiElement)myPlace) &&
93 addCandidate(new GroovyResolveResultImpl(method, resolveContext, substitutor, isAccessible, isStaticsOK));
95 myInapplicableCandidates.add(new GroovyResolveResultImpl(method, resolveContext, substitutor, isAccessible, isStaticsOK));
104 private PsiSubstitutor obtainSubstitutor(PsiSubstitutor substitutor, PsiMethod method, ResolveState state) {
105 final PsiTypeParameter[] typeParameters = method.getTypeParameters();
106 if (myTypeArguments.length == typeParameters.length) {
107 for (int i = 0; i < typeParameters.length; i++) {
108 PsiTypeParameter typeParameter = typeParameters[i];
109 final PsiType typeArgument = myTypeArguments[i];
110 substitutor = substitutor.put(typeParameter, typeArgument);
115 if (argumentsSupplied() && method.hasTypeParameters()) {
116 PsiType[] argTypes = myArgumentTypes;
117 if (method instanceof GrGdkMethod) {
118 assert argTypes != null;
119 //type inference should be performed from static method
120 PsiType[] newArgTypes = new PsiType[argTypes.length + 1];
121 final GroovyPsiElement resolveContext = state.get(RESOLVE_CONTEXT);
122 if (ResolveUtil.isInWithContext(resolveContext)) {
123 newArgTypes[0] = ((GrExpression)resolveContext).getType();
126 newArgTypes[0] = myThisType;
128 System.arraycopy(argTypes, 0, newArgTypes, 1, argTypes.length);
129 argTypes = newArgTypes;
131 method = ((GrGdkMethod) method).getStaticMethod();
132 LOG.assertTrue(method.isValid());
134 return inferMethodTypeParameters(method, substitutor, typeParameters, argTypes);
140 private PsiSubstitutor inferMethodTypeParameters(PsiMethod method, PsiSubstitutor partialSubstitutor, final PsiTypeParameter[] typeParameters, final PsiType[] argTypes) {
141 if (typeParameters.length == 0) return partialSubstitutor;
143 if (argumentsSupplied()) {
144 final GrClosureSignature erasedSignature = GrClosureSignatureUtil.createSignatureWithErasedParameterTypes(method);
146 final GrClosureSignature signature = GrClosureSignatureUtil.createSignature(method, partialSubstitutor);
147 final GrClosureParameter[] params = signature.getParameters();
149 final GrClosureSignatureUtil.ArgInfo<PsiType>[] argInfos =
150 GrClosureSignatureUtil.mapArgTypesToParameters(erasedSignature, argTypes, (GroovyPsiElement)myPlace, myAllVariants);
151 if (argInfos == null) return partialSubstitutor;
153 int max = Math.max(params.length, argTypes.length);
155 PsiType[] parameterTypes = new PsiType[max];
156 PsiType[] argumentTypes = new PsiType[max];
158 for (int paramIndex = 0; paramIndex < argInfos.length; paramIndex++) {
159 PsiType paramType = params[paramIndex].getType();
161 GrClosureSignatureUtil.ArgInfo<PsiType> argInfo = argInfos[paramIndex];
162 if (argInfo != null) {
163 if (argInfo.isMultiArg) {
164 if (paramType instanceof PsiArrayType) paramType = ((PsiArrayType)paramType).getComponentType();
166 for (PsiType type : argInfo.args) {
167 argumentTypes[i] = handleConversion(paramType, type);
168 parameterTypes[i] = paramType;
172 parameterTypes[i] = paramType;
173 argumentTypes[i] = PsiType.NULL;
177 final PsiResolveHelper helper = JavaPsiFacade.getInstance(method.getProject()).getResolveHelper();
178 PsiSubstitutor substitutor = helper.inferTypeArguments(typeParameters, parameterTypes, argumentTypes, LanguageLevel.HIGHEST);
179 for (PsiTypeParameter typeParameter : typeParameters) {
180 if (!substitutor.getSubstitutionMap().containsKey(typeParameter)) {
181 substitutor = inferFromContext(typeParameter, PsiUtil.getSmartReturnType(method), substitutor, helper);
185 return partialSubstitutor.putAll(substitutor);
188 return partialSubstitutor;
191 private PsiType handleConversion(PsiType paramType, PsiType argType) {
192 final GroovyPsiElement context = (GroovyPsiElement)myPlace;
193 if (!TypesUtil.isAssignable(TypeConversionUtil.erasure(paramType), argType, context.getManager(), context.getResolveScope(), false) &&
194 TypesUtil.isAssignableByMethodCallConversion(paramType, argType, context)) {
200 private PsiSubstitutor inferFromContext(PsiTypeParameter typeParameter, PsiType lType, PsiSubstitutor substitutor, PsiResolveHelper helper) {
201 if (myPlace != null) {
202 final PsiType inferred = helper.getSubstitutionForTypeParameter(typeParameter, lType, getContextType(), false, LanguageLevel.HIGHEST);
203 if (inferred != PsiType.NULL) {
204 return substitutor.put(typeParameter, inferred);
211 private PsiType getContextType() {
212 final PsiElement parent = myPlace.getParent().getParent();
213 PsiType rType = null;
214 if (parent instanceof GrReturnStatement) {
215 final GrMethod method = PsiTreeUtil.getParentOfType(parent, GrMethod.class);
216 if (method != null) rType = method.getReturnType();
218 else if (parent instanceof GrAssignmentExpression && myPlace.equals(((GrAssignmentExpression)parent).getRValue())) {
219 rType = ((GrAssignmentExpression)parent).getLValue().getType();
221 else if (parent instanceof GrVariable) {
222 rType = ((GrVariable)parent).getDeclaredType();
228 public GroovyResolveResult[] getCandidates() {
229 if (!myAllVariants && super.hasCandidates()) {
230 return filterCandidates();
232 if (!myInapplicableCandidates.isEmpty()) {
233 final Set<GroovyResolveResult> resultSet =
234 myAllVariants ? myInapplicableCandidates : filterCorrectParameterCount(myInapplicableCandidates);
235 return ResolveUtil.filterSameSignatureCandidates(resultSet, myArgumentTypes != null ? myArgumentTypes.length : -1);
237 return GroovyResolveResult.EMPTY_ARRAY;
240 private Set<GroovyResolveResult> filterCorrectParameterCount(Set<GroovyResolveResult> candidates) {
241 if (myArgumentTypes == null) return candidates;
242 Set<GroovyResolveResult> result = new HashSet<GroovyResolveResult>();
243 for (GroovyResolveResult candidate : candidates) {
244 final PsiElement element = candidate.getElement();
245 if (element instanceof PsiMethod && ((PsiMethod)element).getParameterList().getParametersCount() == myArgumentTypes.length) {
246 result.add(candidate);
249 if (result.size() > 0) return result;
253 private GroovyResolveResult[] filterCandidates() {
254 Set<GroovyResolveResult> array = getCandidatesInternal();
255 if (array.size() == 1) return array.toArray(new GroovyResolveResult[array.size()]);
257 List<GroovyResolveResult> result = new ArrayList<GroovyResolveResult>();
259 Iterator<GroovyResolveResult> itr = array.iterator();
261 result.add(itr.next());
263 GlobalSearchScope scope = myPlace.getResolveScope();
266 while (itr.hasNext()) {
267 GroovyResolveResult resolveResult = itr.next();
268 PsiElement currentElement = resolveResult.getElement();
269 if (currentElement instanceof PsiMethod) {
270 PsiMethod currentMethod = (PsiMethod) currentElement;
271 for (Iterator<GroovyResolveResult> iterator = result.iterator(); iterator.hasNext();) {
272 final GroovyResolveResult otherResolveResult = iterator.next();
273 PsiElement element = otherResolveResult.getElement();
274 if (element instanceof PsiMethod) {
275 PsiMethod method = (PsiMethod) element;
276 final int res = compareMethods(currentMethod, resolveResult.getSubstitutor(), method, otherResolveResult.getSubstitutor(), scope);
287 result.add(resolveResult);
290 return result.toArray(new GroovyResolveResult[result.size()]);
293 private int compareMethods(PsiMethod method1,
294 PsiSubstitutor substitutor1,
296 PsiSubstitutor substitutor2,
297 GlobalSearchScope scope) {
298 if (!method1.getName().equals(method2.getName())) return 0;
300 if (method2 instanceof DominanceAwareMethod && ((DominanceAwareMethod)method2).isMoreConcreteThan(substitutor2, method1, substitutor1, (GroovyPsiElement)myPlace)) {
304 if (method1 instanceof DominanceAwareMethod && ((DominanceAwareMethod)method1).isMoreConcreteThan(substitutor1, method2, substitutor2, (GroovyPsiElement)myPlace)) {
308 if (dominated(method1, substitutor1, method2, substitutor2, scope)) {
311 if (dominated(method2, substitutor2, method1, substitutor1, scope)) {
318 private boolean dominated(PsiMethod method1,
319 PsiSubstitutor substitutor1,
321 PsiSubstitutor substitutor2,
322 GlobalSearchScope scope) { //method1 has more general parameter types thn method2
323 if (!method1.getName().equals(method2.getName())) return false;
325 PsiType[] argTypes = myArgumentTypes;
326 if (method1 instanceof GrGdkMethod && method2 instanceof GrGdkMethod) {
327 method1 = ((GrGdkMethod)method1).getStaticMethod();
328 method2 = ((GrGdkMethod)method2).getStaticMethod();
329 if (myArgumentTypes != null) {
330 argTypes = new PsiType[argTypes.length + 1];
331 System.arraycopy(myArgumentTypes, 0, argTypes, 1, myArgumentTypes.length);
332 argTypes[0] = myThisType;
336 if (myIsConstructor && argTypes != null && argTypes.length == 1) {
337 if (method1.getParameterList().getParametersCount() == 0) return true;
338 if (method2.getParameterList().getParametersCount() == 0) return false;
341 PsiParameter[] params1 = method1.getParameterList().getParameters();
342 PsiParameter[] params2 = method2.getParameterList().getParameters();
343 if (argTypes == null && params1.length != params2.length) return false;
345 if (params1.length < params2.length) {
346 if (params1.length == 0) return false;
347 final PsiType lastType = params1[params1.length - 1].getType(); //varargs applicability
348 return lastType instanceof PsiArrayType;
351 PsiManager manager = method1.getManager();
352 for (int i = 0; i < params2.length; i++) {
353 PsiType type1 = substitutor1.substitute(params1[i].getType());
354 PsiType type2 = substitutor2.substitute(params2[i].getType());
356 if (argTypes != null && argTypes.length > i) {
357 PsiType argType = argTypes[i];
358 if (argType != null) {
359 final boolean converts1 = TypesUtil.isAssignable(type1, argType, manager, scope, false);
360 final boolean converts2 = TypesUtil.isAssignable(type2, argType, manager, scope, false);
361 if (converts1 != converts2) {
367 if (!typesAgree(manager, scope, type1, type2)) return false;
373 private boolean typesAgree(PsiManager manager, GlobalSearchScope scope, PsiType type1, PsiType type2) {
374 if (argumentsSupplied() && type1 instanceof PsiArrayType && !(type2 instanceof PsiArrayType)) {
375 type1 = ((PsiArrayType) type1).getComponentType();
377 return argumentsSupplied() ? //resolve, otherwise same_name_variants
378 TypesUtil.isAssignable(type1, type2, manager, scope, false) :
382 private boolean argumentsSupplied() {
383 return myArgumentTypes != null;
387 public boolean hasCandidates() {
388 return super.hasCandidates() || !myInapplicableCandidates.isEmpty();
391 public boolean hasApplicableCandidates() {
392 return super.hasCandidates();
396 public PsiType[] getArgumentTypes() {
397 return myArgumentTypes;
401 public void handleEvent(Event event, Object associated) {
402 super.handleEvent(event, associated);
403 if (JavaScopeProcessorEvent.CHANGE_LEVEL == event && super.hasCandidates()) {
404 myStopExecuting = true;