From 50297fff8447f16bf98f21c7725803cacb8af431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Afonso=20Janu=C3=A1rio?= Date: Sat, 29 Aug 2026 11:51:45 +0100 Subject: [PATCH] Apply signature hooks to decorator applications visit_decorator_inner calls check_call directly on the decorator's type, so a plugin's get_function_signature_hook or get_method_signature_hook never gets a chance to run: those two only ever fire from inside transform_callee_type, and every other call site that wants them calls that first. get_function_hook and get_method_hook still work fine here, since check_call invokes those two on its own, which is what made the gap easy to miss: half the plugin hook family works on a decorator and half silently doesn't. Calling transform_callee_type on the decorator's type before checking the call closes the gap the same way an ordinary call already gets it. Added a test reusing the existing function_sig_hook fixture, applied as a decorator instead of a plain call, since nothing in the suite exercised a signature hook on a decorator before. --- mypy/checker.py | 4 ++++ test-data/unit/check-custom-plugin.test | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 33ed5387554d8..d9a1f3729c2f4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5864,6 +5864,10 @@ def visit_decorator_inner( object_type = self.lookup_type(d.expr) fullname = self.expr_checker.method_fullname(object_type, d.name) self.check_for_untyped_decorator(e.func, dec, d) + if fullname: + dec = self.expr_checker.transform_callee_type( + fullname, dec, [temp], [nodes.ARG_POS], e, object_type=object_type + ) sig, t2 = self.expr_checker.check_call( dec, [temp], [nodes.ARG_POS], e, callable_name=fullname, object_type=object_type ) diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index dd1de1265b599..b79e752ed5ae4 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -908,6 +908,19 @@ reveal_type(dynamic_signature(b)) # N: Revealed type is "builtins.bytes" \[mypy] plugins=/test-data/unit/plugins/function_sig_hook.py +[case testFunctionSigHookCalledOnDecorator] +# flags: --config-file tmp/mypy.ini + +def dynamic_signature(arg1: str) -> str: ... + +@dynamic_signature +def f() -> None: ... + +reveal_type(f) # N: Revealed type is "def ()" +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/function_sig_hook.py + [case testPluginCalledCorrectlyWhenMethodInDecorator] # flags: --config-file tmp/mypy.ini from typing import TypeVar, Callable