Merge branch 'master' into vlan/pyi
[idea/community.git] / python / src / com / jetbrains / python / psi / impl / PyCallExpressionHelper.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.impl;
17
18 import com.intellij.codeInsight.completion.CompletionUtil;
19 import com.intellij.openapi.util.Pair;
20 import com.intellij.openapi.util.Ref;
21 import com.intellij.psi.PsiElement;
22 import com.intellij.psi.PsiPolyVariantReference;
23 import com.intellij.psi.PsiReference;
24 import com.intellij.psi.util.PsiTreeUtil;
25 import com.jetbrains.python.FunctionParameter;
26 import com.jetbrains.python.PyNames;
27 import com.jetbrains.python.nameResolver.FQNamesProvider;
28 import com.jetbrains.python.nameResolver.NameResolverTools;
29 import com.jetbrains.python.psi.*;
30 import com.jetbrains.python.psi.resolve.PyResolveContext;
31 import com.jetbrains.python.psi.resolve.QualifiedResolveResult;
32 import com.jetbrains.python.psi.types.*;
33 import com.jetbrains.python.toolbox.Maybe;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
36
37 import java.util.*;
38
39 /**
40  * Functions common to different implementors of PyCallExpression, with different base classes.
41  * User: dcheryasov
42  * Date: Dec 23, 2008 10:31:38 AM
43  */
44 public class PyCallExpressionHelper {
45   private PyCallExpressionHelper() {
46   }
47
48   /**
49    * TODO: Copy/Paste with {@link PyArgumentList#addArgument(PyExpression)}
50    * Adds an argument to the end of argument list.
51    *
52    * @param us         the arg list
53    * @param expression what to add
54    */
55   public static void addArgument(PyCallExpression us, PyExpression expression) {
56     final PyArgumentList argumentList = us.getArgumentList();
57     if (argumentList != null) {
58       final PyExpression[] arguments = argumentList.getArguments();
59       final PyExpression last_arg = arguments.length == 0 ? null : arguments[arguments.length - 1];
60       PyElementGenerator.getInstance(us.getProject()).insertItemIntoList(us, last_arg, expression);
61     }
62   }
63
64   /**
65    * Tries to interpret a call as a call to built-in {@code classmethod} or {@code staticmethod}.
66    *
67    * @param redefiningCall the possible call, generally a result of chasing a chain of assignments
68    * @param us             any in-project PSI element, used to determine SDK and ultimately builtins module used to check the wrapping functions
69    * @return a pair of wrapper name and wrapped function; for {@code staticmethod(foo)} it would be ("staticmethod", foo).
70    */
71   @Nullable
72   public static Pair<String, PyFunction> interpretAsModifierWrappingCall(PyCallExpression redefiningCall, PsiElement us) {
73     PyExpression redefining_callee = redefiningCall.getCallee();
74     if (redefiningCall.isCalleeText(PyNames.CLASSMETHOD, PyNames.STATICMETHOD)) {
75       final PyReferenceExpression referenceExpr = (PyReferenceExpression)redefining_callee;
76       if (referenceExpr != null) {
77         final String refName = referenceExpr.getReferencedName();
78         if ((PyNames.CLASSMETHOD.equals(refName) || PyNames.STATICMETHOD.equals(refName))) {
79           PsiElement redefining_func = referenceExpr.getReference().resolve();
80           if (redefining_func != null) {
81             PsiElement true_func = PyBuiltinCache.getInstance(us).getByName(refName);
82             if (true_func instanceof PyClass) true_func = ((PyClass)true_func).findInitOrNew(true, null);
83             if (true_func == redefining_func) {
84               // yes, really a case of "foo = classmethod(foo)"
85               PyArgumentList argumentList = redefiningCall.getArgumentList();
86               if (argumentList != null) { // really can't be any other way
87                 PyExpression[] args = argumentList.getArguments();
88                 if (args.length == 1) {
89                   PyExpression possible_original_ref = args[0];
90                   if (possible_original_ref instanceof PyReferenceExpression) {
91                     PsiElement original = ((PyReferenceExpression)possible_original_ref).getReference().resolve();
92                     if (original instanceof PyFunction) {
93                       // pinned down the original; replace our resolved callee with it and add flags.
94                       return Pair.create(refName, (PyFunction)original);
95                     }
96                   }
97                 }
98               }
99             }
100           }
101         }
102       }
103     }
104     return null;
105   }
106
107   @Nullable
108   public static PyClass resolveCalleeClass(PyCallExpression us) {
109     PyExpression callee = us.getCallee();
110
111     PsiElement resolved;
112     QualifiedResolveResult resolveResult;
113     if (callee instanceof PyReferenceExpression) {
114       // dereference
115       PyReferenceExpression ref = (PyReferenceExpression)callee;
116       resolveResult = ref.followAssignmentsChain(PyResolveContext.noImplicits());
117       resolved = resolveResult.getElement();
118     }
119     else {
120       resolved = callee;
121     }
122     // analyze
123     if (resolved instanceof PyClass) {
124       return (PyClass)resolved;
125     }
126     else if (resolved instanceof PyFunction) {
127       final PyFunction pyFunction = (PyFunction)resolved;
128       return pyFunction.getContainingClass();
129     }
130
131     return null;
132   }
133
134   @Nullable
135   public static PyCallable resolveCalleeFunction(PyCallExpression call, PyResolveContext resolveContext) {
136     PsiElement resolved;
137     PyExpression callee = call.getCallee();
138     if (callee instanceof PyReferenceExpression) {
139       // dereference
140       PyReferenceExpression ref = (PyReferenceExpression)callee;
141       QualifiedResolveResult resolveResult = ref.followAssignmentsChain(resolveContext);
142       resolved = resolveResult.getElement();
143     }
144     else {
145       resolved = callee;
146     }
147     if (resolved instanceof PyClass) {
148       resolved = ((PyClass)resolved).findInitOrNew(true, null); // class to constructor call
149     }
150     else if (resolved instanceof PyCallExpression) {
151       PyCallExpression redefiningCall = (PyCallExpression)resolved;
152       Pair<String, PyFunction> wrapperInfo = interpretAsModifierWrappingCall(redefiningCall, call);
153       if (wrapperInfo != null) {
154         resolved = wrapperInfo.getSecond();
155       }
156     }
157     if (resolved instanceof PyCallable) {
158       return (PyCallable)resolved;
159     }
160     return null;
161   }
162
163
164   @Nullable
165   public static PyCallExpression.PyMarkedCallee resolveCallee(PyCallExpression us, PyResolveContext resolveContext) {
166     return resolveCallee(us, resolveContext, 0);
167   }
168
169   @Nullable
170   public static PyCallExpression.PyMarkedCallee resolveCallee(PyCallExpression us, PyResolveContext resolveContext, int implicitOffset) {
171     PyFunction.Modifier wrappedModifier = null;
172     boolean isConstructorCall = false;
173
174     PyExpression callee = us.getCallee();
175     if (isResolvedToMultipleTargets(callee, resolveContext)) {
176       return null;
177     }
178     PsiElement resolved;
179     QualifiedResolveResult resolveResult = null;
180     if (callee instanceof PyReferenceExpression) {
181       // dereference
182       PyReferenceExpression ref = (PyReferenceExpression)callee;
183       resolveResult = ref.followAssignmentsChain(resolveContext);
184       resolved = resolveResult.getElement();
185     }
186     else {
187       resolved = callee;
188     }
189     // analyze
190     if (resolved instanceof PyClass) {
191       resolved = ((PyClass)resolved).findInitOrNew(true, resolveContext.getTypeEvalContext()); // class to constructor call
192       isConstructorCall = true;
193     }
194     else if (resolved instanceof PyCallExpression) {
195       // is it a case of "foo = classmethod(foo)"?
196       PyCallExpression redefiningCall = (PyCallExpression)resolved;
197       Pair<String, PyFunction> wrapperInfo = interpretAsModifierWrappingCall(redefiningCall, us);
198       if (wrapperInfo != null) {
199         resolved = wrapperInfo.getSecond();
200         String wrapper_name = wrapperInfo.getFirst();
201         if (PyNames.CLASSMETHOD.equals(wrapper_name)) {
202           wrappedModifier = PyFunction.Modifier.CLASSMETHOD;
203         }
204         else if (PyNames.STATICMETHOD.equals(wrapper_name)) wrappedModifier = PyFunction.Modifier.STATICMETHOD;
205       }
206     }
207     final List<PyExpression> qualifiers = resolveResult != null ? resolveResult.getQualifiers() : Collections.<PyExpression>emptyList();
208     final TypeEvalContext context = resolveContext.getTypeEvalContext();
209     if (resolved instanceof PyFunction) {
210       final PyFunction function = (PyFunction)resolved;
211       final Property property = function.getProperty();
212       if (property != null && isQualifiedByInstance(function, qualifiers, context)) {
213         final PyType type = context.getReturnType(function);
214         if (type instanceof PyFunctionTypeImpl) {
215           resolved = ((PyFunctionTypeImpl)type).getCallable();
216         }
217         else {
218           resolved = null;
219         }
220       }
221     }
222     if (resolved instanceof PyCallable) {
223       PyFunction.Modifier modifier = resolved instanceof PyFunction
224                                      ? ((PyFunction)resolved).getModifier()
225                                      : null;
226       if (modifier == null && wrappedModifier != null) {
227         modifier = wrappedModifier;
228       }
229       boolean isByInstance = isConstructorCall || isQualifiedByInstance((PyCallable)resolved, qualifiers, context)
230                              || resolved instanceof PyBoundFunction;
231       PyExpression lastQualifier = qualifiers != null && qualifiers.isEmpty() ? null : qualifiers.get(qualifiers.size() - 1);
232       boolean isByClass = lastQualifier == null ? false : isQualifiedByClass((PyCallable)resolved, lastQualifier, context);
233       final PyCallable callable = (PyCallable)resolved;
234
235       implicitOffset += getImplicitArgumentCount(callable, modifier, isConstructorCall, isByInstance, isByClass);
236       implicitOffset = implicitOffset < 0 ? 0 : implicitOffset; // wrong source can trigger strange behaviour
237       return new PyCallExpression.PyMarkedCallee(callable, modifier, implicitOffset,
238                                                  resolveResult != null ? resolveResult.isImplicit() : false);
239     }
240     return null;
241   }
242
243   private static boolean isResolvedToMultipleTargets(@Nullable PyExpression callee, @NotNull PyResolveContext resolveContext) {
244     if (callee != null) {
245       final List<PsiElement> resolved = PyUtil.multiResolveTopPriority(callee, resolveContext);
246       if (resolved.size() > 1) {
247         return true;
248       }
249     }
250     return false;
251   }
252
253   /**
254    * Calls the {@link #getImplicitArgumentCount(PyExpression, com.jetbrains.python.psi.PyCallable, com.jetbrains.python.psi.PyFunction.Modifier, EnumSet< com.jetbrains.python.psi.PyFunction.Modifier >, boolean) full version}
255    * with null flags and with isByInstance inferred directly from call site (won't work with reassigned bound methods).
256    *
257    * @param callReference       the call site, where arguments are given.
258    * @param functionBeingCalled resolved method which is being called; plain functions are OK but make little sense.
259    * @return a non-negative number of parameters that are implicit to this call.
260    */
261   public static int getImplicitArgumentCount(
262     @NotNull final PyReferenceExpression callReference,
263     @NotNull PyFunction functionBeingCalled) {
264     //return getImplicitArgumentCount(functionBeingCalled, null, null, qualifierIsAnInstance(callReference, TypeEvalContext.fast()));
265     final PyDecorator decorator = PsiTreeUtil.getParentOfType(callReference, PyDecorator.class);
266     if (decorator != null && PsiTreeUtil.isAncestor(decorator.getCallee(), callReference, false)) {
267       return 1;
268     }
269     final PyResolveContext resolveContext = PyResolveContext.noImplicits();
270     QualifiedResolveResult followed = callReference.followAssignmentsChain(resolveContext);
271     boolean isByInstance = isQualifiedByInstance(functionBeingCalled, followed.getQualifiers(), resolveContext.getTypeEvalContext());
272     boolean isByClass = isQualifiedByInstance(functionBeingCalled, followed.getQualifiers(), resolveContext.getTypeEvalContext());
273     return getImplicitArgumentCount(functionBeingCalled, functionBeingCalled.getModifier(), false, isByInstance, isByClass);
274   }
275
276   /**
277    * Finds how many arguments are implicit in a given call.
278    *
279    * @param callable     resolved method which is being called; non-methods immediately return 0.
280    * @param flags        set of flags for the call
281    * @param isByInstance true if the call is known to be by instance (not by class).
282    * @return a non-negative number of parameters that are implicit to this call. E.g. for a typical method call 1 is returned
283    * because one parameter ('self') is implicit.
284    */
285   private static int getImplicitArgumentCount(
286     PyCallable callable,
287     PyFunction.Modifier modifier,
288     boolean isConstructorCall,
289     boolean isByInstance,
290     boolean isByClass
291   ) {
292     int implicit_offset = 0;
293     boolean firstIsArgsOrKwargs = false;
294     final PyParameter[] parameters = callable.getParameterList().getParameters();
295     if (parameters.length > 0) {
296       final PyParameter first = parameters[0];
297       final PyNamedParameter named = first.getAsNamed();
298       if (named != null && (named.isPositionalContainer() || named.isKeywordContainer())) {
299         firstIsArgsOrKwargs = true;
300       }
301     }
302     if (!firstIsArgsOrKwargs && (isByInstance || isConstructorCall)) {
303       implicit_offset += 1;
304     }
305     PyFunction method = callable.asMethod();
306     if (method == null) return implicit_offset;
307
308     if (PyNames.NEW.equals(method.getName())) {
309       return isConstructorCall ? 1 : 0;
310     }
311     if (!isByInstance && !isByClass && PyNames.INIT.equals(method.getName())) {
312       return 1;
313     }
314
315     // decorators?
316     if (modifier == PyFunction.Modifier.STATICMETHOD) {
317       if (isByInstance && implicit_offset > 0) implicit_offset -= 1; // might have marked it as implicit 'self'
318     }
319     else if (modifier == PyFunction.Modifier.CLASSMETHOD) {
320       if (!isByInstance) implicit_offset += 1; // Both Foo.method() and foo.method() have implicit the first arg
321     }
322     return implicit_offset;
323   }
324
325   private static boolean isQualifiedByInstance(PyCallable resolved, List<PyExpression> qualifiers, TypeEvalContext context) {
326     PyDocStringOwner owner = PsiTreeUtil.getStubOrPsiParentOfType(resolved, PyDocStringOwner.class);
327     if (!(owner instanceof PyClass)) {
328       return false;
329     }
330     // true = call by instance
331     if (qualifiers.isEmpty()) {
332       return true; // unqualified + method = implicit constructor call
333     }
334     for (PyExpression qualifier : qualifiers) {
335       if (isQualifiedByInstance(resolved, qualifier, context)) {
336         return true;
337       }
338     }
339     return false;
340   }
341
342   private static boolean isQualifiedByInstance(PyCallable resolved, PyExpression qualifier, TypeEvalContext context) {
343     if (isQualifiedByClass(resolved, qualifier, context)) {
344       return false;
345     }
346     PyType qtype = context.getType(qualifier);
347     if (qtype != null) {
348       // TODO: handle UnionType
349       if (qtype instanceof PyModuleType) return false; // qualified by module, not instance.
350     }
351     return true; // NOTE. best guess: unknown qualifier is more probably an instance.
352   }
353
354   private static boolean isQualifiedByClass(PyCallable resolved, PyExpression qualifier, TypeEvalContext context) {
355     PyType qtype = context.getType(qualifier);
356     if (qtype instanceof PyClassType) {
357       if (((PyClassType)qtype).isDefinition()) {
358         PyClass resolvedParent = PsiTreeUtil.getStubOrPsiParentOfType(resolved, PyClass.class);
359         if (resolvedParent != null) {
360           final PyClass qualifierClass = ((PyClassType)qtype).getPyClass();
361           if ((qualifierClass.isSubclass(resolvedParent) || resolvedParent.isSubclass(qualifierClass))) {
362             return true;
363           }
364         }
365       }
366     }
367     else if (qtype instanceof PyClassLikeType) {
368       return ((PyClassLikeType)qtype).isDefinition(); //Any definition means callable is classmethod
369     }
370     return false;
371   }
372
373   static boolean isCalleeText(PyCallExpression pyCallExpression, String[] nameCandidates) {
374     final PyExpression callee = pyCallExpression.getCallee();
375     if (!(callee instanceof PyReferenceExpression)) {
376       return false;
377     }
378     for (String name : nameCandidates) {
379       if (name.equals(((PyReferenceExpression)callee).getReferencedName())) {
380         return true;
381       }
382     }
383     return false;
384   }
385
386
387   /**
388    * Returns argument if it exists and has appropriate type
389    * @param parameter  argument
390    * @param argClass   expected class
391    * @param expression call expression
392    * @param <T>        expected class
393    * @return argument expression or null if has wrong type of does not exist
394    */
395   @Nullable
396   public static <T extends PsiElement> T getArgument(
397     @NotNull final FunctionParameter parameter,
398     @NotNull final Class<T> argClass,
399     @NotNull final PyCallExpression expression) {
400     final PyArgumentList list = expression.getArgumentList();
401     if (list == null) {
402       return null;
403     }
404     return PyUtil.as(list.getValueExpressionForParam(parameter), argClass);
405   }
406
407   @Nullable
408   public static PyExpression getKeywordArgument(PyCallExpression expr, String keyword) {
409     for (PyExpression arg : expr.getArguments()) {
410       if (arg instanceof PyKeywordArgument) {
411         PyKeywordArgument kwarg = (PyKeywordArgument)arg;
412         if (keyword.equals(kwarg.getKeyword())) {
413           return kwarg.getValueExpression();
414         }
415       }
416     }
417     return null;
418   }
419
420   public static PyType getCallType(@NotNull PyCallExpression call, @NotNull TypeEvalContext context) {
421     if (!TypeEvalStack.mayEvaluate(call)) {
422       return null;
423     }
424     try {
425       PyExpression callee = call.getCallee();
426       if (callee instanceof PyReferenceExpression) {
427         // hardwired special cases
428         if (PyNames.SUPER.equals(callee.getText())) {
429           final Maybe<PyType> superCallType = getSuperCallType(call, context);
430           if (superCallType.isDefined()) {
431             return superCallType.value();
432           }
433         }
434         if ("type".equals(callee.getText())) {
435           final PyExpression[] args = call.getArguments();
436           if (args.length == 1) {
437             final PyExpression arg = args[0];
438             final PyType argType = context.getType(arg);
439             if (argType instanceof PyClassType) {
440               final PyClassType classType = (PyClassType)argType;
441               if (!classType.isDefinition()) {
442                 final PyClass cls = classType.getPyClass();
443                 return context.getType(cls);
444               }
445             }
446             else {
447               return null;
448             }
449           }
450         }
451         // normal cases
452         final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context);
453         final PsiPolyVariantReference reference = ((PyReferenceExpression)callee).getReference(resolveContext);
454         final List<PyType> members = new ArrayList<PyType>();
455         for (PsiElement target : PyUtil.multiResolveTopPriority(reference)) {
456           if (target != null) {
457             final Ref<? extends PyType> typeRef = getCallTargetReturnType(call, target, context);
458             if (typeRef != null) {
459               members.add(typeRef.get());
460             }
461           }
462         }
463         if (!members.isEmpty()) {
464           return PyUnionType.union(members);
465         }
466       }
467       if (callee == null) {
468         return null;
469       }
470       else {
471         final PyType type = context.getType(callee);
472         if (type instanceof PyCallableType) {
473           final PyCallableType callableType = (PyCallableType)type;
474           return callableType.getCallType(context, call);
475         }
476         return null;
477       }
478     }
479     finally {
480       TypeEvalStack.evaluated(call);
481     }
482   }
483
484   @Nullable
485   private static Ref<? extends PyType> getCallTargetReturnType(@NotNull PyCallExpression call, @NotNull PsiElement target,
486                                                                @NotNull TypeEvalContext context) {
487     PyClass cls = null;
488     PyFunction init = null;
489     if (target instanceof PyClass) {
490       cls = (PyClass)target;
491       init = cls.findInitOrNew(true, context);
492     }
493     else if (target instanceof PyFunction) {
494       final PyFunction f = (PyFunction)target;
495       if (PyNames.INIT.equals(f.getName())) {
496         init = f;
497         cls = f.getContainingClass();
498       }
499     }
500     if (init != null) {
501       final PyType t = init.getCallType(context, call);
502       if (cls != null) {
503         if (init.getContainingClass() != cls) {
504           if (t instanceof PyCollectionType) {
505             final PyType elementType = ((PyCollectionType)t).getElementType(context);
506             return Ref.create(new PyCollectionTypeImpl(cls, false, elementType));
507           }
508           return Ref.create(new PyClassTypeImpl(cls, false));
509         }
510       }
511       if (t != null && !(t instanceof PyNoneType)) {
512         return Ref.create(t);
513       }
514       if (cls != null && t == null) {
515         final PyFunction newMethod = cls.findMethodByName(PyNames.NEW, true);
516         if (newMethod != null && !PyBuiltinCache.getInstance(call).isBuiltin(newMethod)) {
517           return Ref.create(PyUnionType.createWeakType(new PyClassTypeImpl(cls, false)));
518         }
519       }
520     }
521     if (cls != null) {
522       return Ref.create(new PyClassTypeImpl(cls, false));
523     }
524     final PyType providedType = PyReferenceExpressionImpl.getReferenceTypeFromProviders(target, context, call);
525     if (providedType instanceof PyCallableType) {
526       return Ref.create(((PyCallableType)providedType).getCallType(context, call));
527     }
528     if (target instanceof PyCallable) {
529       final PyCallable callable = (PyCallable)target;
530       return Ref.create(callable.getCallType(context, call));
531     }
532     return null;
533   }
534
535   @NotNull
536   private static Maybe<PyType> getSuperCallType(@NotNull PyCallExpression call, TypeEvalContext context) {
537     final PyExpression callee = call.getCallee();
538     if (callee instanceof PyReferenceExpression) {
539       PsiElement must_be_super_init = ((PyReferenceExpression)callee).getReference().resolve();
540       if (must_be_super_init instanceof PyFunction) {
541         PyClass must_be_super = ((PyFunction)must_be_super_init).getContainingClass();
542         if (must_be_super == PyBuiltinCache.getInstance(call).getClass(PyNames.SUPER)) {
543           final PyArgumentList argumentList = call.getArgumentList();
544           if (argumentList != null) {
545             final PyClass containingClass = PsiTreeUtil.getParentOfType(call, PyClass.class);
546             PyExpression[] args = argumentList.getArguments();
547             if (args.length > 1) {
548               PyExpression first_arg = args[0];
549               if (first_arg instanceof PyReferenceExpression) {
550                 final PyReferenceExpression firstArgRef = (PyReferenceExpression)first_arg;
551                 final PyExpression qualifier = firstArgRef.getQualifier();
552                 if (qualifier != null && PyNames.__CLASS__.equals(firstArgRef.getReferencedName())) {
553                   final PsiReference qRef = qualifier.getReference();
554                   final PsiElement element = qRef == null ? null : qRef.resolve();
555                   if (element instanceof PyParameter) {
556                     final PyParameterList parameterList = PsiTreeUtil.getParentOfType(element, PyParameterList.class);
557                     if (parameterList != null && element == parameterList.getParameters()[0]) {
558                       return new Maybe<PyType>(getSuperCallTypeForArguments(context, containingClass, args[1]));
559                     }
560                   }
561                 }
562                 PsiElement possible_class = firstArgRef.getReference().resolve();
563                 if (possible_class instanceof PyClass && ((PyClass)possible_class).isNewStyleClass(null)) {
564                   final PyClass first_class = (PyClass)possible_class;
565                   return new Maybe<PyType>(getSuperCallTypeForArguments(context, first_class, args[1]));
566                 }
567               }
568             }
569             else if ((call.getContainingFile() instanceof PyFile) &&
570                      ((PyFile)call.getContainingFile()).getLanguageLevel().isPy3K() &&
571                      (containingClass != null)) {
572               return new Maybe<PyType>(getSuperClassUnionType(containingClass));
573             }
574           }
575         }
576       }
577     }
578     return new Maybe<PyType>();
579   }
580
581   @Nullable
582   private static PyType getSuperCallTypeForArguments(TypeEvalContext context, PyClass firstClass, PyExpression second_arg) {
583     // check 2nd argument, too; it should be an instance
584     if (second_arg != null) {
585       PyType second_type = context.getType(second_arg);
586       if (second_type instanceof PyClassType) {
587         // imitate isinstance(second_arg, possible_class)
588         PyClass secondClass = ((PyClassType)second_type).getPyClass();
589         if (CompletionUtil.getOriginalOrSelf(firstClass) == secondClass) {
590           return getSuperClassUnionType(firstClass);
591         }
592         if (secondClass.isSubclass(firstClass)) {
593           final Iterator<PyClass> iterator = firstClass.getAncestorClasses(context).iterator();
594           if (iterator.hasNext()) {
595             return new PyClassTypeImpl(iterator.next(), false); // super(Foo, self) has type of Foo, modulo __get__()
596           }
597         }
598       }
599     }
600     return null;
601   }
602
603   @Nullable
604   private static PyType getSuperClassUnionType(@NotNull PyClass pyClass) {
605     // TODO: this is closer to being correct than simply taking first superclass type but still not entirely correct;
606     // super can also delegate to sibling types
607     // TODO handle __mro__ here
608     final PyClass[] supers = pyClass.getSuperClasses();
609     if (supers.length > 0) {
610       if (supers.length == 1) {
611         return new PyClassTypeImpl(supers[0], false);
612       }
613       List<PyType> superTypes = new ArrayList<PyType>();
614       for (PyClass aSuper : supers) {
615         superTypes.add(new PyClassTypeImpl(aSuper, false));
616       }
617       return PyUnionType.union(superTypes);
618     }
619     return null;
620   }
621
622   /**
623    * Checks if expression callee's name matches one of names, provided by appropriate {@link com.jetbrains.python.nameResolver.FQNamesProvider}
624    *
625    * @param expression     call expression
626    * @param namesProviders name providers to check name against
627    * @return true if matches
628    * @see com.jetbrains.python.nameResolver
629    */
630   public static boolean isCallee(@NotNull final PyCallExpression expression, @NotNull final FQNamesProvider... namesProviders) {
631     final PyExpression callee = expression.getCallee();
632     return (callee != null) && NameResolverTools.isName(callee, namesProviders);
633   }
634 }