From 74e49cc475a51dbfbe479614592df7dda22c881c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Perrin?= Date: Thu, 10 Mar 2022 10:26:00 +0000 Subject: [PATCH 1/2] Test for undefined FuncDef.arguments after deserialize from cache --- test-data/unit/check-modules.test | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) 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 From 9cc69b8deaf8bab6970e87fba865dd124dd7aceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Perrin?= Date: Mon, 27 Jun 2022 11:45:06 +0100 Subject: [PATCH 2/2] Check FuncDef.arguments before use When deserializing from cache, FuncDef.arguments is not set, so check before use and fallback to arg_names. --- mypy/messages.py | 4 +++- mypy/nodes.py | 2 +- mypy/types.py | 19 +++++++++---------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 1d6641c00a61..628c2cbaf0a4 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1978,7 +1978,9 @@ 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 + hasattr(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]: diff --git a/mypy/nodes.py b/mypy/nodes.py index abc8666e390d..96d40d3af560 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 unset if deserialized (type is a lie!) 'arg_names', # Names of arguments 'arg_kinds', # Kinds of arguments 'min_args', # Minimum number of arguments diff --git a/mypy/types.py b/mypy/types.py index f0f7add2d92f..78643907f5cc 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1524,16 +1524,15 @@ 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.arg_names and + definition.info and + not definition.is_static): + if getattr(definition, 'arguments', None): + first_arg = definition.arguments[0].variable.name + else: + first_arg = definition.arg_names[0] + self.def_extras = {'first_arg': first_arg} else: self.def_extras = {} self.type_guard = type_guard