From a9a83bb8d3b2386b63983413cf7ccecba0eab336 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sun, 19 Nov 2023 22:47:04 -0500 Subject: [PATCH 1/7] Don't check plugin-generated functions --- mypy/checker.py | 10 ++++++---- mypy/nodes.py | 6 ++++++ mypy/plugins/attrs.py | 4 ++++ test-data/unit/check-flags.test | 4 ++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index b9a9d3affb90..1fa18ebc9711 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1066,6 +1066,9 @@ def check_func_item( If type_override is provided, use it as the function type. """ + if defn.is_plugin_generated(): + return + self.dynamic_funcs.append(defn.is_dynamic() and not type_override) with self.enter_partial_types(is_function=True): @@ -1324,9 +1327,7 @@ def check_func_def( ) # Ignore plugin generated methods, these usually don't need any bodies. - if defn.info is not FUNC_NO_INFO and ( - defn.name not in defn.info.names or defn.info.names[defn.name].plugin_generated - ): + if defn.is_plugin_generated(): show_error = False # Ignore also definitions that appear in `if TYPE_CHECKING: ...` blocks. @@ -4911,7 +4912,8 @@ def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None sig, t2 = self.expr_checker.check_call( dec, [temp], [nodes.ARG_POS], e, callable_name=fullname, object_type=object_type ) - self.check_untyped_after_decorator(sig, e.func) + if e.decorators: + self.check_untyped_after_decorator(sig, e.func) sig = set_callable_name(sig, e.func) e.var.type = sig e.var.is_ready = True diff --git a/mypy/nodes.py b/mypy/nodes.py index 17e06613d1e3..79a1d1876982 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -710,6 +710,12 @@ def __init__( if self.arguments[i] is None and i < self.max_fixed_argc(): self.min_args = i + 1 + def is_plugin_generated(self) -> bool: + if self.info is FUNC_NO_INFO: + return False + sym = self.info.names.get(self.name) + return not sym or sym.plugin_generated + def max_fixed_argc(self) -> int: return self.max_pos diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 3ddc234a7e4a..3913dea15459 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -628,6 +628,10 @@ def _attribute_from_attrib_maker( converter = convert converter_info = _parse_converter(ctx, converter) + if init_type is None and ctx.api.options.disallow_incomplete_defs: + assert lhs.node is not None + ctx.api.msg.need_annotation_for_var(lhs.node, stmt) + name = unmangle(lhs.name) return Attribute( name, ctx.cls.info, attr_has_default, init, kw_only, converter_info, stmt, init_type diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 04adaca317c1..2a060fb67839 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1328,9 +1328,9 @@ class Annotated: import attrs @attrs.define -class PartiallyAnnotated: # E: Function is missing a type annotation for one or more arguments +class PartiallyAnnotated: bar: int = attrs.field() - baz = attrs.field() + baz = attrs.field() # E: Need type annotation for "baz" [builtins fixtures/plugin_attrs.pyi] From 0b7aee937bb13ef7c74cec6871e5a267e29e8946 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sun, 19 Nov 2023 22:50:00 -0500 Subject: [PATCH 2/7] undo unintentional change --- mypy/checker.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 1fa18ebc9711..2e12e6dc8ff4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4912,8 +4912,7 @@ def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None sig, t2 = self.expr_checker.check_call( dec, [temp], [nodes.ARG_POS], e, callable_name=fullname, object_type=object_type ) - if e.decorators: - self.check_untyped_after_decorator(sig, e.func) + self.check_untyped_after_decorator(sig, e.func) sig = set_callable_name(sig, e.func) e.var.type = sig e.var.is_ready = True From 8a4479662a4821bdc264ef88a9f6b084fc0fa81a Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sun, 19 Nov 2023 23:36:58 -0500 Subject: [PATCH 3/7] update test --- mypy/plugins/attrs.py | 2 +- test-data/unit/check-flags.test | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 3913dea15459..8047578d09eb 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -628,7 +628,7 @@ def _attribute_from_attrib_maker( converter = convert converter_info = _parse_converter(ctx, converter) - if init_type is None and ctx.api.options.disallow_incomplete_defs: + if init_type is None and ctx.api.options.disallow_untyped_defs: assert lhs.node is not None ctx.api.msg.need_annotation_for_var(lhs.node, stmt) diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 2a060fb67839..3d8255629793 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1303,18 +1303,18 @@ def f(i: int, s): main:3: error: Function is missing a return type annotation main:3: error: Function is missing a type annotation for one or more arguments -[case testDisallowIncompleteDefsAttrsNoAnnotations] -# flags: --disallow-incomplete-defs +[case testDisallowUntypedDefsAttrsNoAnnotations] +# flags: --disallow-untyped-defs import attrs @attrs.define class Unannotated: - foo = attrs.field() + foo = attrs.field() # E: Need type annotation for "foo" [builtins fixtures/plugin_attrs.pyi] -[case testDisallowIncompleteDefsAttrsWithAnnotations] -# flags: --disallow-incomplete-defs +[case testDisallowUntypedDefsAttrsWithAnnotations] +# flags: --disallow-untyped-defs import attrs @attrs.define @@ -1323,8 +1323,8 @@ class Annotated: [builtins fixtures/plugin_attrs.pyi] -[case testDisallowIncompleteDefsAttrsPartialAnnotations] -# flags: --disallow-incomplete-defs +[case testDisallowUntypedDefsAttrsPartialAnnotations] +# flags: --disallow-untyped-defs import attrs @attrs.define From 525280b3255358c4e687ece97190534750734d85 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Thu, 23 Nov 2023 09:59:34 -0500 Subject: [PATCH 4/7] rename is_plugin_generated to is_synthetic --- mypy/checker.py | 4 ++-- mypy/nodes.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 2e12e6dc8ff4..c94dc9c0d3d1 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1066,7 +1066,7 @@ def check_func_item( If type_override is provided, use it as the function type. """ - if defn.is_plugin_generated(): + if defn.is_synthetic(): return self.dynamic_funcs.append(defn.is_dynamic() and not type_override) @@ -1327,7 +1327,7 @@ def check_func_def( ) # Ignore plugin generated methods, these usually don't need any bodies. - if defn.is_plugin_generated(): + if defn.is_synthetic(): show_error = False # Ignore also definitions that appear in `if TYPE_CHECKING: ...` blocks. diff --git a/mypy/nodes.py b/mypy/nodes.py index 79a1d1876982..81e242c68fbf 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -710,7 +710,7 @@ def __init__( if self.arguments[i] is None and i < self.max_fixed_argc(): self.min_args = i + 1 - def is_plugin_generated(self) -> bool: + def is_synthetic(self) -> bool: if self.info is FUNC_NO_INFO: return False sym = self.info.names.get(self.name) From 33d06ccdca82263344f33aff6e28977a837ffe75 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sun, 10 Dec 2023 23:46:59 -0500 Subject: [PATCH 5/7] treat builtins overrides as typeshed files --- mypy/build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mypy/build.py b/mypy/build.py index 961198fc2fa4..79630de9355b 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -837,6 +837,8 @@ def parse_file( self.errors.ignored_files.add(path) tree = parse(source, path, id, self.errors, options=options) tree._fullname = id + if options.use_builtins_fixtures and id in CORE_BUILTIN_MODULES: + tree._is_typeshed_file = True self.add_stats( files_parsed=1, modules_parsed=int(not tree.is_stub), From 8980c84ffdaf91d520ea767150e0a4f7f47a4f73 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sun, 10 Dec 2023 23:49:55 -0500 Subject: [PATCH 6/7] avoid checking decorators for synthetic functions --- mypy/checker.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 0e658e51527b..0cc5897c6283 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4876,6 +4876,8 @@ def visit_del_stmt(self, s: DelStmt) -> None: ) def visit_decorator(self, e: Decorator) -> None: + if e.func.is_synthetic(): + return for d in e.decorators: if isinstance(d, RefExpr): if d.fullname == "typing.no_type_check": From fcd3e0a76b9c52c7cf2cfa6dd2c9c01e3f45e5be Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sun, 10 Dec 2023 23:50:20 -0500 Subject: [PATCH 7/7] add regression test for #16454 --- test-data/unit/check-dataclasses.test | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 107298875761..5b5221dfd58d 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2544,3 +2544,18 @@ class Base: class Child(Base): y: int [builtins fixtures/dataclasses.pyi] + +[case testDataclassDisallowAny] +# flags: --disallow-any-explicit --disallow-any-decorated + +from dataclasses import dataclass +from typing import Any + +# Ensures that when Any-typed fields end up in synthetic functions, +# those functions are not flagged since there's nothing the user can do about that +@dataclass +class C: + a: Any # E: Explicit "Any" is not allowed + +[typing fixtures/typing-full.pyi] +[builtins fixtures/dataclasses.pyi]