diff --git a/mypy/messages.py b/mypy/messages.py index 1d6641c00a61..4bafe85da0fc 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1978,10 +1978,12 @@ def [T <: int] f(self, x: int, y: T) -> None s += ' = ...' # If we got a "special arg" (i.e: self, cls, etc...), prepend it to the arg list - if isinstance(tp.definition, FuncDef) and tp.definition.name is not None: + if (isinstance(tp.definition, FuncDef) and + tp.definition.name is not None and + tp.definition.arguments): definition_args = [arg.variable.name for arg in tp.definition.arguments] - if definition_args and tp.arg_names != definition_args \ - and len(definition_args) > 0 and definition_args[0]: + if (tp.arg_names != definition_args and + definition_args[0]): if s: s = ', ' + s s = definition_args[0] + s diff --git a/mypy/nodes.py b/mypy/nodes.py index abc8666e390d..f3e7fef34749 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -649,7 +649,7 @@ def set_line(self, class FuncItem(FuncBase): """Base class for nodes usable as overloaded function items.""" - __slots__ = ('arguments', # Note that can be None if deserialized (type is a lie!) + __slots__ = ('arguments', # Note that can be empty if deserialized 'arg_names', # Names of arguments 'arg_kinds', # Kinds of arguments 'min_args', # Minimum number of arguments @@ -665,7 +665,7 @@ class FuncItem(FuncBase): 'expanded', # Variants of function with type variables with values expanded ) - __deletable__ = ('arguments', 'max_pos', 'min_args') + __deletable__ = ('max_pos', 'min_args') def __init__(self, arguments: Optional[List[Argument]] = None, @@ -780,7 +780,6 @@ def deserialize(cls, data: JsonDict) -> 'FuncDef': ret.arg_names = data['arg_names'] ret.arg_kinds = [ArgKind(x) for x in data['arg_kinds']] # Leave these uninitialized so that future uses will trigger an error - del ret.arguments del ret.max_pos del ret.min_args return ret diff --git a/mypy/types.py b/mypy/types.py index f0f7add2d92f..dc78fdbdd931 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1524,16 +1524,13 @@ def __init__(self, # after serialization, but it is useful in error messages. # TODO: decide how to add more info here (file, line, column) # without changing interface hash. - self.def_extras = { - 'first_arg': ( - definition.arguments[0].variable.name - if (getattr(definition, 'arguments', None) - and definition.arg_names - and definition.info - and not definition.is_static) - else None - ), - } + first_arg: Optional[str] = None + if definition.info and not definition.is_static: + if definition.arguments: + first_arg = definition.arguments[0].variable.name + elif definition.arg_names: + first_arg = definition.arg_names[0] + self.def_extras = {'first_arg': first_arg} else: self.def_extras = {} self.type_guard = type_guard diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index 576eacc141df..9c59104fd7ce 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -142,10 +142,10 @@ def fdef_to_sig(self, fdef: FuncDef) -> FuncSignature: # the sole way that FuncDecl arguments are tracked. This is # generally fine except in some cases (like for computing # init_sig) we need to produce FuncSignatures from a - # deserialized FuncDef that lacks arguments. We won't ever - # need to use those inside of a FuncIR, so we just make up - # some crap. - if hasattr(fdef, 'arguments'): + # deserialized FuncDef where arguments is the empty list. We + # won't ever need to use those inside of a FuncIR, so we just + # make up some crap. + if fdef.arguments: arg_names = [arg.variable.name for arg in fdef.arguments] else: arg_names = [name or '' for name in fdef.arg_names] diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 67767a9114e1..fc9ddedecc69 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -3190,3 +3190,35 @@ from dir1 import * from .test2 import * [file dir1/test2.py] from test1 import aaaa # E: Module "test1" has no attribute "aaaa" + +[case testIncompatibleOverrideFromCachedModuleIncremental] +import b +[file a.py] +class Foo: + def frobnicate(self, *args, **kwargs): pass +[file b.py] +from a import Foo +class Bar(Foo): + def frobnicate(self) -> None: pass +[file b.py.2] +from a import Foo +class Bar(Foo): + def frobnicate(self, *args) -> None: pass +[file b.py.3] +from a import Foo +class Bar(Foo): + def frobnicate(self, *args) -> None: pass # type: ignore[override] # I know +[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[out1] +tmp/b.py:3: error: Signature of "frobnicate" incompatible with supertype "Foo" +tmp/b.py:3: note: Superclass: +tmp/b.py:3: note: def frobnicate(self, *args: Any, **kwargs: Any) -> Any +tmp/b.py:3: note: Subclass: +tmp/b.py:3: note: def frobnicate(self) -> None +[out2] +tmp/b.py:3: error: Signature of "frobnicate" incompatible with supertype "Foo" +tmp/b.py:3: note: Superclass: +tmp/b.py:3: note: def frobnicate(self, *args: Any, **kwargs: Any) -> Any +tmp/b.py:3: note: Subclass: +tmp/b.py:3: note: def frobnicate(self, *args: Any) -> None