Repository: python/mypy

Issue:
Refreshing synthetic types causes fine-grained crash
It looks like we definitely *do* need to handle non-TypeList synthetic types in merge.

These two tests fail now but passed before #4652:
```
[case testReprocessEllipses1]
import a
[file a.py]
from typing import Tuple
def foo(x: Tuple[int, ...]) -> None: pass
[file a.py.2]
from typing import Tuple
def foo(x: Tuple[int, ...]) -> None: pass
[builtins fixtures/tuple.pyi]
[out]
==

[case testReprocessEllipses2]
import a
[file a.py]
from typing import Callable
def foo(x: Callable[..., int]) -> None: pass
[file a.py.2]
from typing import Callable
def foo(x: Callable[..., int]) -> None: pass
[out]
==
```

This one now fails with a synthetic type visitor error (and fails with `assert symbol.kind != UNBOUND_IMPORTED` before #4652):
```
[case testReprocessCallableArg]
import a
[file a.py]
from typing import Callable
from mypy_extensions import Arg
def a(f: Callable[[Arg(int, 'x')], int]) -> None: pass
[file a.py.2]
from typing import Callable
from mypy_extensions import Arg
def a(f: Callable[[Arg(int, 'x')], int]) -> None: pass
[out]
==
```


Discussion:

Resolving patch:
diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py
--- a/mypy/server/astmerge.py
+++ b/mypy/server/astmerge.py
@@ -55,9 +55,9 @@
 )
 from mypy.traverser import TraverserVisitor
 from mypy.types import (
-    Type, TypeVisitor, Instance, AnyType, NoneTyp, CallableType, DeletedType, PartialType,
+    Type, SyntheticTypeVisitor, Instance, AnyType, NoneTyp, CallableType, DeletedType, PartialType,
     TupleType, TypeType, TypeVarType, TypedDictType, UnboundType, UninhabitedType, UnionType,
-    Overloaded, TypeVarDef, TypeList
+    Overloaded, TypeVarDef, TypeList, CallableArgument, EllipsisType, StarType
 )
 from mypy.util import get_prefix
 
@@ -307,7 +307,7 @@ def replace_statements(self, nodes: List[Statement]) -> List[Statement]:
         return result
 
 
-class TypeReplaceVisitor(TypeVisitor[None]):
+class TypeReplaceVisitor(SyntheticTypeVisitor[None]):
     """Similar to NodeReplaceVisitor, but for type objects."""
 
     def __init__(self, replacements: Dict[SymbolNode, SymbolNode]) -> None:
@@ -376,6 +376,15 @@ def visit_type_list(self, typ: TypeList) -> None:
         for item in typ.items:
             item.accept(self)
 
+    def visit_callable_argument(self, typ: CallableArgument) -> None:
+        typ.typ.accept(self)
+
+    def visit_ellipsis_type(self, typ: EllipsisType) -> None:
+        pass
+
+    def visit_star_type(self, typ: StarType) -> None:
+        typ.type.accept(self)
+
     def visit_uninhabited_type(self, typ: UninhabitedType) -> None:
         pass
 
diff --git a/mypy/types.py b/mypy/types.py
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -255,6 +255,7 @@ def __init__(self, items: List[Type], line: int = -1, column: int = -1) -> None:
         self.items = items
 
     def accept(self, visitor: 'TypeVisitor[T]') -> T:
+        assert isinstance(visitor, SyntheticTypeVisitor)
         return visitor.visit_type_list(self)
 
     def serialize(self) -> JsonDict:
@@ -1506,11 +1507,6 @@ def visit_type_type(self, t: TypeType) -> T:
     def visit_forwardref_type(self, t: ForwardRef) -> T:
         raise RuntimeError('Internal error: unresolved forward reference')
 
-    def visit_type_list(self, t: TypeList) -> T:
-        # TODO: Do we need to implement this in more visitors? TypeList objects can
-        #   exist as components of UnboundTypes.
-        raise self._notimplemented_helper('type_list')
-
 
 class SyntheticTypeVisitor(TypeVisitor[T]):
     """A TypeVisitor that also knows how to visit synthetic AST constructs.


Test patch:
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -2531,3 +2531,40 @@ x = ''
 b.py:5: error: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[Tuple[int, ...], Any], int]")
 ==
 b.py:5: error: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[Tuple[int, ...], Any], int]")
+
+[case testReprocessEllipses1]
+import a
+[file a.py]
+from typing import Tuple
+def foo(x: Tuple[int, ...]) -> None: pass
+[file a.py.2]
+from typing import Tuple
+def foo(x: Tuple[int, ...]) -> None: pass
+[builtins fixtures/tuple.pyi]
+[out]
+==
+
+[case testReprocessEllipses2]
+import a
+[file a.py]
+from typing import Callable
+def foo(x: Callable[..., int]) -> None: pass
+[file a.py.2]
+from typing import Callable
+def foo(x: Callable[..., int]) -> None: pass
+[out]
+==
+
+[case testReprocessCallableArg]
+import a
+[file a.py]
+from typing import Callable
+from mypy_extensions import Arg
+def a(f: Callable[[Arg(int, 'x')], int]) -> None: pass
+[file a.py.2]
+from typing import Callable
+from mypy_extensions import Arg
+def a(f: Callable[[Arg(int, 'x')], int]) -> None: pass
+[builtins fixtures/dict.pyi]
+[out]
+==