Return next after Base in self's MRO for super(Base, self) (PY-32533)

Previously first class in `Base`'s MRO were returned.
This commit is contained in:
Semyon Proshev
2018-11-09 20:06:25 +03:00
parent cfffd077af
commit 0de4368592
2 changed files with 33 additions and 3 deletions
@@ -659,9 +659,15 @@ public class PyCallExpressionHelper {
return getSuperClassUnionType(firstClass, context);
}
if (secondClass.isSubclass(firstClass, context)) {
final Iterator<PyClass> iterator = firstClass.getAncestorClasses(context).iterator();
if (iterator.hasNext()) {
return new PyClassTypeImpl(iterator.next(), false); // super(Foo, self) has type of Foo, modulo __get__()
final PyClass nextAfterFirstInMro = StreamEx
.of(secondClass.getAncestorClasses(context))
.dropWhile(it -> it != firstClass)
.skip(1)
.findFirst()
.orElse(null);
if (nextAfterFirstInMro != null) {
return new PyClassTypeImpl(nextAfterFirstInMro, false);
}
}
}
@@ -3294,6 +3294,30 @@ public class PyTypeTest extends PyTestCase {
" expr = a");
}
// PY-32533
public void testSuperWithAnotherType() {
runWithLanguageLevel(
LanguageLevel.PYTHON34,
() -> doTest("A",
"class A:\n" +
" def f(self):\n" +
" return 'A'\n" +
"\n" +
"class B:\n" +
" def f(self):\n" +
" return 'B'\n" +
"\n" +
"class C(B):\n" +
" def f(self):\n" +
" return 'C'\n" +
"\n" +
"class D(C, A):\n" +
" def f(self):\n" +
" expr = super(B, self)\n" +
" return expr.f()")
);
}
private static List<TypeEvalContext> getTypeEvalContexts(@NotNull PyExpression element) {
return ImmutableList.of(TypeEvalContext.codeAnalysis(element.getProject(), element.getContainingFile()).withTracing(),
TypeEvalContext.userInitiated(element.getProject(), element.getContainingFile()).withTracing());