Merge branch 'master' into vlan/pyi
[idea/community.git] / python / src / com / jetbrains / python / psi / types / PyTypeChecker.java
1 /*
2  * Copyright 2000-2014 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 com.jetbrains.python.psi.types;
17
18 import com.intellij.openapi.extensions.Extensions;
19 import com.intellij.psi.PsiElement;
20 import com.intellij.psi.PsiPolyVariantReference;
21 import com.intellij.psi.PsiReference;
22 import com.intellij.psi.ResolveResult;
23 import com.intellij.util.ArrayUtil;
24 import com.jetbrains.python.PyNames;
25 import com.jetbrains.python.codeInsight.PyCustomMember;
26 import com.jetbrains.python.psi.*;
27 import com.jetbrains.python.psi.impl.PyBuiltinCache;
28 import com.jetbrains.python.psi.resolve.PyResolveContext;
29 import com.jetbrains.python.psi.resolve.RatedResolveResult;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
32
33 import java.util.*;
34
35 /**
36  * @author vlan
37  */
38 public class PyTypeChecker {
39   private PyTypeChecker() {
40   }
41
42   public static boolean match(@Nullable PyType expected, @Nullable PyType actual, @NotNull TypeEvalContext context) {
43     return match(expected, actual, context, null, true);
44   }
45
46   /**
47    * Checks whether a type *actual* can be placed where *expected* is expected.
48    * For example int matches object, while str doesn't match int.
49    * Work for builtin types, classes, tuples etc.
50    *
51    * @param expected expected type
52    * @param actual type to be matched against expected
53    * @param context
54    * @param substitutions
55    * @return
56    */
57   public static boolean match(@Nullable PyType expected, @Nullable PyType actual, @NotNull TypeEvalContext context,
58                               @Nullable Map<PyGenericType, PyType> substitutions) {
59     return match(expected, actual, context, substitutions, true);
60   }
61
62   private static boolean match(@Nullable PyType expected, @Nullable PyType actual, @NotNull TypeEvalContext context,
63                                @Nullable Map<PyGenericType, PyType> substitutions, boolean recursive) {
64     // TODO: subscriptable types?, module types?, etc.
65     if (expected instanceof PyGenericType && substitutions != null) {
66       final PyGenericType generic = (PyGenericType)expected;
67       final PyType subst = substitutions.get(generic);
68       final PyType bound = generic.getBound();
69       if (!match(bound, actual, context, substitutions, recursive)) {
70         return false;
71       }
72       else if (subst != null) {
73         if (expected.equals(actual)) {
74           return true;
75         }
76         else if (recursive) {
77           return match(subst, actual, context, substitutions, false);
78         }
79         else {
80           return false;
81         }
82       }
83       else if (actual != null) {
84         substitutions.put(generic, actual);
85       }
86       else if (bound != null) {
87         substitutions.put(generic, bound);
88       }
89       return true;
90     }
91     if (expected == null || actual == null) {
92       return true;
93     }
94     if (expected instanceof PyClassType) {
95       final PyClass c = ((PyClassType)expected).getPyClass();
96       if ("object".equals(c.getName())) {
97         return true;
98       }
99     }
100     if (isUnknown(actual)) {
101       return true;
102     }
103     if (actual instanceof PyUnionType) {
104       for (PyType m : ((PyUnionType)actual).getMembers()) {
105         if (match(expected, m, context, substitutions, recursive)) {
106           return true;
107         }
108       }
109       return false;
110     }
111     if (expected instanceof PyUnionType) {
112       for (PyType t : ((PyUnionType)expected).getMembers()) {
113         if (match(t, actual, context, substitutions, recursive)) {
114           return true;
115         }
116       }
117       return false;
118     }
119     if (expected instanceof PyClassType && actual instanceof PyClassType) {
120       final PyClass superClass = ((PyClassType)expected).getPyClass();
121       final PyClass subClass = ((PyClassType)actual).getPyClass();
122       if (expected instanceof PyCollectionType && actual instanceof PyCollectionType) {
123         if (!matchClasses(superClass, subClass, context)) {
124           return false;
125         }
126         final PyType superElementType = ((PyCollectionType)expected).getElementType(context);
127         final PyType subElementType = ((PyCollectionType)actual).getElementType(context);
128         return match(superElementType, subElementType, context, substitutions, recursive);
129       }
130       else if (expected instanceof PyTupleType && actual instanceof PyTupleType) {
131         final PyTupleType superTupleType = (PyTupleType)expected;
132         final PyTupleType subTupleType = (PyTupleType)actual;
133         if (superTupleType.getElementCount() != subTupleType.getElementCount()) {
134           return false;
135         }
136         else {
137           for (int i = 0; i < superTupleType.getElementCount(); i++) {
138             if (!match(superTupleType.getElementType(i), subTupleType.getElementType(i), context, substitutions, recursive)) {
139               return false;
140             }
141           }
142           return true;
143         }
144       }
145       else if (matchClasses(superClass, subClass, context)) {
146         return true;
147       }
148       else if (((PyClassType)actual).isDefinition() && PyNames.CALLABLE.equals(expected.getName())) {
149         return true;
150       }
151       if (expected.equals(actual)) {
152         return true;
153       }
154     }
155     if (actual instanceof PyFunctionTypeImpl && expected instanceof PyClassType) {
156       final PyClass superClass = ((PyClassType)expected).getPyClass();
157       if (PyNames.CALLABLE.equals(superClass.getName())) {
158         return true;
159       }
160     }
161     if (actual instanceof PyStructuralType && ((PyStructuralType)actual).isInferredFromUsages()) {
162       return true;
163     }
164     if (expected instanceof PyStructuralType && actual instanceof PyStructuralType) {
165       final PyStructuralType expectedStructural = (PyStructuralType)expected;
166       final PyStructuralType actualStructural = (PyStructuralType)actual;
167       if (expectedStructural.isInferredFromUsages()) {
168         return true;
169       }
170       return expectedStructural.getAttributeNames().containsAll(actualStructural.getAttributeNames());
171     }
172     if (expected instanceof PyStructuralType && actual instanceof PyClassType) {
173       final PyClassType actualClassType = (PyClassType)actual;
174       if (overridesGetAttr(actualClassType.getPyClass(), context)) {
175         return true;
176       }
177       final Set<String> actualAttributes = getClassTypeAttributes(actualClassType, true, context);
178       return actualAttributes.containsAll(((PyStructuralType)expected).getAttributeNames());
179     }
180     if (actual instanceof PyStructuralType && expected instanceof PyClassType) {
181       final Set<String> expectedAttributes = getClassTypeAttributes((PyClassType)expected, true, context);
182       return expectedAttributes.containsAll(((PyStructuralType)actual).getAttributeNames());
183     }
184     if (actual instanceof PyCallableType && expected instanceof PyCallableType) {
185       final PyCallableType expectedCallable = (PyCallableType)expected;
186       final PyCallableType actualCallable = (PyCallableType)actual;
187       if (expectedCallable.isCallable() && actualCallable.isCallable()) {
188         final List<PyCallableParameter> expectedParameters = expectedCallable.getParameters(context);
189         final List<PyCallableParameter> actualParameters = actualCallable.getParameters(context);
190         if (expectedParameters != null && actualParameters != null) {
191           final int size = Math.min(expectedParameters.size(), actualParameters.size());
192           for (int i = 0; i < size; i++) {
193             final PyCallableParameter expectedParam = expectedParameters.get(i);
194             final PyCallableParameter actualParam = actualParameters.get(i);
195             // TODO: Check named and star params, not only positional ones
196             if (!match(expectedParam.getType(context), actualParam.getType(context), context, substitutions, recursive)) {
197               return false;
198             }
199           }
200         }
201         if (!match(expectedCallable.getReturnType(context), actualCallable.getReturnType(context), context, substitutions, recursive)) {
202           return false;
203         }
204         return true;
205       }
206     }
207     return matchNumericTypes(expected, actual);
208   }
209
210   @NotNull
211   public static Set<String> getClassTypeAttributes(@NotNull PyClassType type, boolean inherited, @NotNull TypeEvalContext context) {
212     final Set<String> attributes = getClassAttributes(type.getPyClass(), inherited, context);
213     for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
214       final Collection<PyCustomMember> members = provider.getMembers(type, null);
215       for (PyCustomMember member : members) {
216         attributes.add(member.getName());
217       }
218     }
219     return attributes;
220   }
221
222   @NotNull
223   private static Set<String> getClassAttributes(@NotNull PyClass cls, boolean inherited, @NotNull TypeEvalContext context) {
224     final Set<String> attributes = new HashSet<String>();
225     for (PyFunction function : cls.getMethods(false)) {
226       attributes.add(function.getName());
227     }
228     for (PyTargetExpression instanceAttribute : cls.getInstanceAttributes()) {
229       attributes.add(instanceAttribute.getName());
230     }
231     for (PyTargetExpression classAttribute : cls.getClassAttributes()) {
232       attributes.add(classAttribute.getName());
233     }
234     if (inherited) {
235       for (PyClass ancestor : cls.getAncestorClasses(null)) {
236         final PyType ancestorType = context.getType(ancestor);
237         if (ancestorType instanceof PyClassType) {
238           attributes.addAll(getClassTypeAttributes((PyClassType)ancestorType, false, context));
239         }
240       }
241     }
242     return attributes;
243   }
244
245   private static boolean matchNumericTypes(PyType expected, PyType actual) {
246     final String superName = expected.getName();
247     final String subName = actual.getName();
248     final boolean subIsBool = "bool".equals(subName);
249     final boolean subIsInt = "int".equals(subName);
250     final boolean subIsLong = "long".equals(subName);
251     final boolean subIsFloat = "float".equals(subName);
252     final boolean subIsComplex = "complex".equals(subName);
253     if (superName == null || subName == null ||
254         superName.equals(subName) ||
255         ("int".equals(superName) && subIsBool) ||
256         (("long".equals(superName) || PyNames.ABC_INTEGRAL.equals(superName)) && (subIsBool || subIsInt)) ||
257         (("float".equals(superName) || PyNames.ABC_REAL.equals(superName)) && (subIsBool || subIsInt || subIsLong)) ||
258         (("complex".equals(superName) || PyNames.ABC_COMPLEX.equals(superName)) && (subIsBool || subIsInt || subIsLong || subIsFloat)) ||
259         (PyNames.ABC_NUMBER.equals(superName) && (subIsBool || subIsInt || subIsLong || subIsFloat || subIsComplex))) {
260       return true;
261     }
262     return false;
263   }
264
265   public static boolean isUnknown(@Nullable PyType type) {
266     if (type == null || type instanceof PyGenericType) {
267       return true;
268     }
269     if (type instanceof PyUnionType) {
270       final PyUnionType union = (PyUnionType)type;
271       for (PyType t : union.getMembers()) {
272         if (isUnknown(t)) {
273           return true;
274         }
275       }
276     }
277     return false;
278   }
279
280   @Nullable
281   public static PyType toNonWeakType(@Nullable PyType type, @NotNull TypeEvalContext context) {
282     if (type instanceof PyUnionType) {
283       final PyUnionType unionType = (PyUnionType)type;
284       if (unionType.isWeak()) {
285         return unionType.excludeNull(context);
286       }
287     }
288     return type;
289   }
290
291   public static boolean hasGenerics(@Nullable PyType type, @NotNull TypeEvalContext context) {
292     final Set<PyGenericType> collected = new HashSet<PyGenericType>();
293     collectGenerics(type, context, collected, new HashSet<PyType>());
294     return !collected.isEmpty();
295   }
296
297   private static void collectGenerics(@Nullable PyType type, @NotNull TypeEvalContext context, @NotNull Set<PyGenericType> collected,
298                                       @NotNull Set<PyType> visited) {
299     if (visited.contains(type)) {
300       return;
301     }
302     visited.add(type);
303     if (type instanceof PyGenericType) {
304       collected.add((PyGenericType)type);
305     }
306     else if (type instanceof PyUnionType) {
307       final PyUnionType union = (PyUnionType)type;
308       for (PyType t : union.getMembers()) {
309         collectGenerics(t, context, collected, visited);
310       }
311     }
312     else if (type instanceof PyCollectionType) {
313       final PyCollectionType collection = (PyCollectionType)type;
314       collectGenerics(collection.getElementType(context), context, collected, visited);
315     }
316     else if (type instanceof PyTupleType) {
317       final PyTupleType tuple = (PyTupleType)type;
318       final int n = tuple.getElementCount();
319       for (int i = 0; i < n; i++) {
320         collectGenerics(tuple.getElementType(i), context, collected, visited);
321       }
322     }
323     else if (type instanceof PyCallableType) {
324       final PyCallableType callable = (PyCallableType)type;
325       final List<PyCallableParameter> parameters = callable.getParameters(context);
326       if (parameters != null) {
327         for (PyCallableParameter parameter : parameters) {
328           if (parameter != null) {
329             collectGenerics(parameter.getType(context), context, collected, visited);
330           }
331         }
332       }
333       collectGenerics(callable.getReturnType(context), context, collected, visited);
334     }
335   }
336
337   @Nullable
338   public static PyType substitute(@Nullable PyType type, @NotNull Map<PyGenericType, PyType> substitutions,
339                                   @NotNull TypeEvalContext context) {
340     if (hasGenerics(type, context)) {
341       if (type instanceof PyGenericType) {
342         return substitutions.get((PyGenericType)type);
343       }
344       else if (type instanceof PyUnionType) {
345         final PyUnionType union = (PyUnionType)type;
346         final List<PyType> results = new ArrayList<PyType>();
347         for (PyType t : union.getMembers()) {
348           final PyType subst = substitute(t, substitutions, context);
349           results.add(subst);
350         }
351         return PyUnionType.union(results);
352       }
353       else if (type instanceof PyCollectionTypeImpl) {
354         final PyCollectionTypeImpl collection = (PyCollectionTypeImpl)type;
355         final PyType elem = collection.getElementType(context);
356         final PyType subst = substitute(elem, substitutions, context);
357         return new PyCollectionTypeImpl(collection.getPyClass(), collection.isDefinition(), subst);
358       }
359       else if (type instanceof PyTupleType) {
360         final PyTupleType tuple = (PyTupleType)type;
361         final int n = tuple.getElementCount();
362         final List<PyType> results = new ArrayList<PyType>();
363         for (int i = 0; i < n; i++) {
364           final PyType subst = substitute(tuple.getElementType(i), substitutions, context);
365           results.add(subst);
366         }
367         return new PyTupleType((PyTupleType)type, results.toArray(new PyType[results.size()]));
368       }
369       else if (type instanceof PyCallableType) {
370         final PyCallableType callable = (PyCallableType)type;
371         List<PyCallableParameter> substParams = null;
372         final List<PyCallableParameter> parameters = callable.getParameters(context);
373         if (parameters != null) {
374           substParams = new ArrayList<PyCallableParameter>();
375           for (PyCallableParameter parameter : parameters) {
376             final PyType substType = substitute(parameter.getType(context), substitutions, context);
377             final PyCallableParameter subst = parameter.getParameter() != null ?
378                                               new PyCallableParameterImpl(parameter.getParameter()) :
379                                               new PyCallableParameterImpl(parameter.getName(), substType);
380             substParams.add(subst);
381           }
382         }
383         final PyType substResult = substitute(callable.getReturnType(context), substitutions, context);
384         return new PyCallableTypeImpl(substParams, substResult);
385       }
386     }
387     return type;
388   }
389
390   @Nullable
391   public static Map<PyGenericType, PyType> unifyGenericCall(@Nullable PyExpression receiver,
392                                                             @NotNull Map<PyExpression, PyNamedParameter> arguments,
393                                                             @NotNull TypeEvalContext context) {
394     final Map<PyGenericType, PyType> substitutions = unifyReceiver(receiver, context);
395     for (Map.Entry<PyExpression, PyNamedParameter> entry : arguments.entrySet()) {
396       final PyNamedParameter p = entry.getValue();
397       if (p.isPositionalContainer() || p.isKeywordContainer()) {
398         continue;
399       }
400       final PyType argType = context.getType(entry.getKey());
401       final PyType paramType = context.getType(p);
402       if (!match(paramType, argType, context, substitutions)) {
403         return null;
404       }
405     }
406     return substitutions;
407   }
408
409   @NotNull
410   public static Map<PyGenericType, PyType> unifyReceiver(@Nullable PyExpression receiver, @NotNull TypeEvalContext context) {
411     final Map<PyGenericType, PyType> substitutions = new LinkedHashMap<PyGenericType, PyType>();
412     // Collect generic params of object type
413     final Set<PyGenericType> generics = new LinkedHashSet<PyGenericType>();
414     final PyType qualifierType = receiver != null ? context.getType(receiver) : null;
415     collectGenerics(qualifierType, context, generics, new HashSet<PyType>());
416     for (PyGenericType t : generics) {
417       substitutions.put(t, t);
418     }
419     // Unify generics in constructor
420     if (qualifierType != null) {
421       final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context);
422       // TODO: Resolve to __new__ as well
423       final List<? extends RatedResolveResult> results = qualifierType.resolveMember(PyNames.INIT, null, AccessDirection.READ,
424                                                                                      resolveContext);
425       if (results != null && !results.isEmpty()) {
426         final PsiElement init = results.get(0).getElement();
427         if (init instanceof PyTypedElement) {
428           final PyType initType = context.getType((PyTypedElement)init);
429           if (initType instanceof PyCallableType) {
430             final PyType initReturnType = ((PyCallableType)initType).getReturnType(context);
431             if (initReturnType != null) {
432               match(initReturnType, qualifierType, context, substitutions);
433             }
434           }
435         }
436       }
437     }
438     return substitutions;
439   }
440
441   private static boolean matchClasses(@Nullable PyClass superClass, @Nullable PyClass subClass, @NotNull TypeEvalContext context) {
442     if (superClass == null || subClass == null || subClass.isSubclass(superClass) || PyABCUtil.isSubclass(subClass, superClass)) {
443       return true;
444     }
445     else if (PyUtil.hasUnresolvedAncestors(subClass, context)) {
446       return true;
447     }
448     else {
449       final String superName = superClass.getName();
450       return superName != null && superName.equals(subClass.getName());
451     }
452   }
453
454   @Nullable
455   public static AnalyzeCallResults analyzeCall(@NotNull PyCallExpression call, @NotNull TypeEvalContext context) {
456     final PyExpression callee = call.getCallee();
457     final PyArgumentList args = call.getArgumentList();
458     if (args != null) {
459       final CallArgumentsMapping mapping = args.analyzeCall(PyResolveContext.noImplicits().withTypeEvalContext(context));
460       final Map<PyExpression, PyNamedParameter> arguments = mapping.getPlainMappedParams();
461       final PyCallExpression.PyMarkedCallee markedCallee = mapping.getMarkedCallee();
462       if (markedCallee != null) {
463         final PyCallable callable = markedCallee.getCallable();
464         if (callable instanceof PyFunction) {
465           final PyFunction function = (PyFunction)callable;
466           final PyExpression receiver;
467           if (function.getModifier() == PyFunction.Modifier.STATICMETHOD) {
468             receiver = null;
469           }
470           else if (callee instanceof PyQualifiedExpression) {
471             receiver = ((PyQualifiedExpression)callee).getQualifier();
472           }
473           else {
474             receiver = null;
475           }
476           return new AnalyzeCallResults(callable, receiver, arguments);
477         }
478       }
479     }
480     return null;
481   }
482
483   @Nullable
484   public static AnalyzeCallResults analyzeCall(@NotNull PyBinaryExpression expr, @NotNull TypeEvalContext context) {
485     final PsiPolyVariantReference ref = expr.getReference(PyResolveContext.noImplicits().withTypeEvalContext(context));
486     final ResolveResult[] resolveResult;
487     resolveResult = ref.multiResolve(false);
488     AnalyzeCallResults firstResults = null;
489     for (ResolveResult result : resolveResult) {
490       final PsiElement resolved = result.getElement();
491       if (resolved instanceof PyTypedElement) {
492         final PyTypedElement typedElement = (PyTypedElement)resolved;
493         final PyType type = context.getType(typedElement);
494         if (!(type instanceof PyFunctionTypeImpl)) {
495           return null;
496         }
497         final PyCallable callable = ((PyFunctionTypeImpl)type).getCallable();
498         final String operatorName = typedElement.getName();
499         final boolean isRight = PyNames.isRightOperatorName(operatorName);
500         final PyExpression arg = isRight ? expr.getLeftExpression() : expr.getRightExpression();
501         final PyExpression receiver = isRight ? expr.getRightExpression() : expr.getLeftExpression();
502         final PyParameter[] parameters = callable.getParameterList().getParameters();
503         if (parameters.length >= 2) {
504           final PyNamedParameter param = parameters[1].getAsNamed();
505           if (arg != null && param != null) {
506             final Map<PyExpression, PyNamedParameter> arguments = new LinkedHashMap<PyExpression, PyNamedParameter>();
507             arguments.put(arg, param);
508             final AnalyzeCallResults results = new AnalyzeCallResults(callable, receiver, arguments);
509             if (firstResults == null) {
510               firstResults = results;
511             }
512             if (match(context.getType(param), context.getType(arg), context)) {
513               return results;
514             }
515           }
516         }
517       }
518     }
519     if (firstResults != null) {
520       return firstResults;
521     }
522     return null;
523   }
524
525   @Nullable
526   public static AnalyzeCallResults analyzeCall(@NotNull PySubscriptionExpression expr, @NotNull TypeEvalContext context) {
527     final PsiReference ref = expr.getReference(PyResolveContext.noImplicits().withTypeEvalContext(context));
528     final PsiElement resolved;
529     resolved = ref.resolve();
530     if (resolved instanceof PyTypedElement) {
531       final PyType type = context.getType((PyTypedElement)resolved);
532       if (type instanceof PyFunctionType) {
533         final PyCallable callable = ((PyFunctionType)type).getCallable();
534         final PyParameter[] parameters = callable.getParameterList().getParameters();
535         if (parameters.length == 2) {
536           final PyNamedParameter param = parameters[1].getAsNamed();
537           if (param != null) {
538             final Map<PyExpression, PyNamedParameter> arguments = new LinkedHashMap<PyExpression, PyNamedParameter>();
539             final PyExpression arg = expr.getIndexExpression();
540             if (arg != null) {
541               arguments.put(arg, param);
542               return new AnalyzeCallResults(callable, expr.getOperand(), arguments);
543             }
544           }
545         }
546       }
547     }
548     return null;
549   }
550
551   @Nullable
552   public static AnalyzeCallResults analyzeCallSite(@Nullable PyCallSiteExpression callSite, @NotNull TypeEvalContext context) {
553     if (callSite instanceof PyCallExpression) {
554       return analyzeCall((PyCallExpression)callSite, context);
555     }
556     else if (callSite instanceof PyBinaryExpression) {
557       return analyzeCall((PyBinaryExpression)callSite, context);
558     }
559     else if (callSite instanceof PySubscriptionExpression) {
560       return analyzeCall((PySubscriptionExpression)callSite, context);
561     }
562     return null;
563   }
564
565   @Nullable
566   public static Boolean isCallable(@Nullable PyType type) {
567     if (type == null) {
568       return null;
569     }
570     else if (type instanceof PyUnionType) {
571       Boolean result = true;
572       for (PyType member : ((PyUnionType)type).getMembers()) {
573         final Boolean callable = isCallable(member);
574         if (callable == null) {
575           return null;
576         }
577         else if (!callable) {
578           result = false;
579         }
580       }
581       return result;
582     }
583     else if (type instanceof PyCallableType) {
584       return ((PyCallableType) type).isCallable();
585     }
586     else if (type instanceof PyStructuralType && ((PyStructuralType)type).isInferredFromUsages()) {
587       return true;
588     }
589     return false;
590   }
591
592   public static boolean overridesGetAttr(@NotNull PyClass cls, @NotNull TypeEvalContext context) {
593     PsiElement method = resolveClassMember(cls, PyNames.GETATTR, context);
594     if (method != null) {
595       return true;
596     }
597     method = resolveClassMember(cls, PyNames.GETATTRIBUTE, context);
598     if (method != null && !PyBuiltinCache.getInstance(cls).isBuiltin(method)) {
599       return true;
600     }
601     return false;
602   }
603
604   @Nullable
605   private static PsiElement resolveClassMember(@NotNull PyClass cls, @NotNull String name, @NotNull TypeEvalContext context) {
606     final PyType type = context.getType(cls);
607     if (type != null) {
608       final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context);
609       final List<? extends RatedResolveResult> results = type.resolveMember(name, null, AccessDirection.READ, resolveContext);
610       if (results != null && !results.isEmpty()) {
611         return results.get(0).getElement();
612       }
613     }
614     return null;
615   }
616
617   @Nullable
618   public static PyType getTargetTypeFromTupleAssignment(@NotNull PyTargetExpression target, @NotNull PyTupleExpression parentTuple,
619                                                         @NotNull PyTupleType assignedTupleType) {
620     final int count = assignedTupleType.getElementCount();
621     final PyExpression[] elements = parentTuple.getElements();
622     if (elements.length == count) {
623       final int index = ArrayUtil.indexOf(elements, target);
624       if (index >= 0) {
625         return assignedTupleType.getElementType(index);
626       }
627       for (int i = 0; i < count; i++) {
628         PyExpression element = elements[i];
629         while (element instanceof PyParenthesizedExpression) {
630           element = ((PyParenthesizedExpression)element).getContainedExpression();
631         }
632         if (element instanceof PyTupleExpression) {
633           final PyType elementType = assignedTupleType.getElementType(i);
634           if (elementType instanceof PyTupleType) {
635             final PyType result = getTargetTypeFromTupleAssignment(target, (PyTupleExpression)element, (PyTupleType)elementType);
636             if (result != null) {
637               return result;
638             }
639           }
640         }
641       }
642     }
643     return null;
644   }
645
646   public static class AnalyzeCallResults {
647     @NotNull private final PyCallable myCallable;
648     @Nullable private final PyExpression myReceiver;
649     @NotNull private final Map<PyExpression, PyNamedParameter> myArguments;
650
651     public AnalyzeCallResults(@NotNull PyCallable callable, @Nullable PyExpression receiver,
652                               @NotNull Map<PyExpression, PyNamedParameter> arguments) {
653       myCallable = callable;
654       myReceiver = receiver;
655       myArguments = arguments;
656     }
657
658     @NotNull
659     public PyCallable getCallable() {
660       return myCallable;
661     }
662
663     @Nullable
664     public PyExpression getReceiver() {
665       return myReceiver;
666     }
667
668     @NotNull
669     public Map<PyExpression, PyNamedParameter> getArguments() {
670       return myArguments;
671     }
672   }
673 }