Repository: python/mypy

Issue:
Crash on unpacking after invalid base class
Simplest way to reproduce:

``` python
class X(1):
    ...

x = X()
a, b = x
```

Output:

```
t_test6.py:1: error: Invalid base class
t_test6.py:5: error: INTERNAL ERROR -- please report a bug at https://github.com/python/mypy/issues
Traceback (most recent call last):
  File "/usr/local/bin/mypy", line 6, in <module>
    main(__file__)
  File "/usr/local/lib/python3.4/dist-packages/mypy/main.py", line 38, in main
    res = type_check_only(sources, bin_dir, options)
  File "/usr/local/lib/python3.4/dist-packages/mypy/main.py", line 79, in type_check_only
    options=options)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 181, in build
    dispatch(sources, manager)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1470, in dispatch
    process_graph(graph, manager)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1650, in process_graph
    process_stale_scc(graph, scc)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1729, in process_stale_scc
    graph[id].type_check()
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1415, in type_check
    manager.type_checker.visit_file(self.tree, self.xpath, self.options)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 165, in visit_file
    self.accept(d)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 217, in accept
    typ = node.accept(self)
  File "/usr/local/lib/python3.4/dist-packages/mypy/nodes.py", line 796, in accept
    return visitor.visit_assignment_stmt(self)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 1028, in visit_assignment_stmt
    self.check_assignment(s.lvalues[-1], s.rvalue, s.type is None, s.new_syntax)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 1044, in check_assignment
    infer_lvalue_type)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 1129, in check_assignment_to_multiple_lvalues
    self.check_multi_assignment(lvalues, rvalue, context, infer_lvalue_type)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 1165, in check_multi_assignment
    context, infer_lvalue_type)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 1257, in check_multi_assignment_from_iterable
    item_type = self.iterable_item_type(cast(Instance, rvalue_type))
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 2321, in iterable_item_type
    self.lookup_typeinfo('typing.Iterable'))
  File "/usr/local/lib/python3.4/dist-packages/mypy/maptype.py", line 23, in map_instance_to_supertype
    return map_instance_to_supertypes(instance, superclass)[0]
IndexError: list index out of range
t_test6.py:5: note: use --pdb to drop into pdb
```

This could be related to #1065

> `class_derivation_paths` that finds all the paths up the hierarchy from a derived class to a given ancestor class. The code implicitly assumes that this function will only ever return a single path

But in this case there are no path, rather than two paths.

Crash on unpacking after invalid base class
Simplest way to reproduce:

``` python
class X(1):
    ...

x = X()
a, b = x
```

Output:

```
t_test6.py:1: error: Invalid base class
t_test6.py:5: error: INTERNAL ERROR -- please report a bug at https://github.com/python/mypy/issues
Traceback (most recent call last):
  File "/usr/local/bin/mypy", line 6, in <module>
    main(__file__)
  File "/usr/local/lib/python3.4/dist-packages/mypy/main.py", line 38, in main
    res = type_check_only(sources, bin_dir, options)
  File "/usr/local/lib/python3.4/dist-packages/mypy/main.py", line 79, in type_check_only
    options=options)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 181, in build
    dispatch(sources, manager)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1470, in dispatch
    process_graph(graph, manager)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1650, in process_graph
    process_stale_scc(graph, scc)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1729, in process_stale_scc
    graph[id].type_check()
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1415, in type_check
    manager.type_checker.visit_file(self.tree, self.xpath, self.options)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 165, in visit_file
    self.accept(d)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 217, in accept
    typ = node.accept(self)
  File "/usr/local/lib/python3.4/dist-packages/mypy/nodes.py", line 796, in accept
    return visitor.visit_assignment_stmt(self)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 1028, in visit_assignment_stmt
    self.check_assignment(s.lvalues[-1], s.rvalue, s.type is None, s.new_syntax)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 1044, in check_assignment
    infer_lvalue_type)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 1129, in check_assignment_to_multiple_lvalues
    self.check_multi_assignment(lvalues, rvalue, context, infer_lvalue_type)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 1165, in check_multi_assignment
    context, infer_lvalue_type)
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 1257, in check_multi_assignment_from_iterable
    item_type = self.iterable_item_type(cast(Instance, rvalue_type))
  File "/usr/local/lib/python3.4/dist-packages/mypy/checker.py", line 2321, in iterable_item_type
    self.lookup_typeinfo('typing.Iterable'))
  File "/usr/local/lib/python3.4/dist-packages/mypy/maptype.py", line 23, in map_instance_to_supertype
    return map_instance_to_supertypes(instance, superclass)[0]
IndexError: list index out of range
t_test6.py:5: note: use --pdb to drop into pdb
```

This could be related to #1065

> `class_derivation_paths` that finds all the paths up the hierarchy from a derived class to a given ancestor class. The code implicitly assumes that this function will only ever return a single path

But in this case there are no path, rather than two paths.



Discussion:
Thanks for the report! Do you know if there's a way this can happen without something as unlikely as `1` for a base class?

@gnprice It fails exactly the same way for all invalid bases. For example (note the typo):

``` python
from typing import Typo

class X(Typo):
    ...

x = X()
a, b = x
```

also it fails for bare `Union` etc:

``` python
from typing import Union

class X(Union):
    ...

x = X()
a, b = x
```

@gnprice Another examples are `Union[str, int]`, `Any`, bare `NewType`. I think this would happen for all invalid bases. Probably, an even more simple way to reproduce this is:

``` python
class X(SomeTypo):
    ...
a, b = X()
```

I'll take this. Crashes like this are usually best dealt with by adding a None check somewhere. There are probably many different scenarios to get a crash by using a class involved in an error, and I don't want to go hunting for them, but this seems a pretty common scenario given that it's any invalid base class and not _too_ strange a use afterwards.

Thanks for the report! Do you know if there's a way this can happen without something as unlikely as `1` for a base class?

@gnprice It fails exactly the same way for all invalid bases. For example (note the typo):

``` python
from typing import Typo

class X(Typo):
    ...

x = X()
a, b = x
```

also it fails for bare `Union` etc:

``` python
from typing import Union

class X(Union):
    ...

x = X()
a, b = x
```

@gnprice Another examples are `Union[str, int]`, `Any`, bare `NewType`. I think this would happen for all invalid bases. Probably, an even more simple way to reproduce this is:

``` python
class X(SomeTypo):
    ...
a, b = X()
```

I'll take this. Crashes like this are usually best dealt with by adding a None check somewhere. There are probably many different scenarios to get a crash by using a class involved in an error, and I don't want to go hunting for them, but this seems a pretty common scenario given that it's any invalid base class and not _too_ strange a use afterwards.


Resolving patch:
diff --git a/mypy/maptype.py b/mypy/maptype.py
--- a/mypy/maptype.py
+++ b/mypy/maptype.py
@@ -36,7 +36,11 @@ def map_instance_to_supertypes(instance: Instance,
                 a.extend(map_instance_to_direct_supertypes(t, sup))
             types = a
         result.extend(types)
-    return result
+    if result:
+        return result
+    else:
+        # Nothing. Presumably due to an error. Construct a dummy using Any.
+        return [Instance(supertype, [AnyType()] * len(supertype.type_vars))]
 
 
 def class_derivation_paths(typ: TypeInfo,


Test patch:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -1419,6 +1419,11 @@ C(0)
 C(arg=0)
 [out]
 
+[case testErrorMapToSupertype]
+import typing
+class X(Nope): pass  # E: Name 'Nope' is not defined
+a, b = X()  # Used to crash here (#2244)
+
 
 -- Type[C]
 -- -------