diff --git a/mypy/checker.py b/mypy/checker.py index c1c31538b7de..87bd9915af97 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -6793,6 +6793,9 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool: ) return not watcher.has_new_errors() + def get_expression_type(self, node: Expression, type_context: Type | None = None) -> Type: + return self.expr_checker.accept(node, type_context=type_context) + class CollectArgTypeVarTypes(TypeTraverserVisitor): """Collects the non-nested argument types in a set.""" diff --git a/mypy/plugin.py b/mypy/plugin.py index cf124b45d04f..4d62c2bd184b 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -250,6 +250,11 @@ def named_generic_type(self, name: str, args: list[Type]) -> Instance: """Construct an instance of a builtin type with given type arguments.""" raise NotImplementedError + @abstractmethod + def get_expression_type(self, node: Expression, type_context: Type | None = None) -> Type: + """Checks the type of the given expression.""" + raise NotImplementedError + @trait class SemanticAnalyzerPluginInterface: diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index afd9423d6820..e05a8e44b2f8 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -9,7 +9,6 @@ import mypy.plugin # To avoid circular imports. from mypy.applytype import apply_generic_arguments -from mypy.checker import TypeChecker from mypy.errorcodes import LITERAL_REQ from mypy.expandtype import expand_type, expand_type_by_instance from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type @@ -1048,13 +1047,7 @@ def evolve_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl return ctx.default_signature # leave it to the type checker to complain inst_arg = ctx.args[0][0] - - # - assert isinstance(ctx.api, TypeChecker) - inst_type = ctx.api.expr_checker.accept(inst_arg) - # - - inst_type = get_proper_type(inst_type) + inst_type = get_proper_type(ctx.api.get_expression_type(inst_arg)) inst_type_str = format_type_bare(inst_type, ctx.api.options) attr_types = _get_expanded_attr_types(ctx, inst_type, inst_type, inst_type) @@ -1074,14 +1067,10 @@ def evolve_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl def fields_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> CallableType: """Provide the signature for `attrs.fields`.""" - if not ctx.args or len(ctx.args) != 1 or not ctx.args[0] or not ctx.args[0][0]: + if len(ctx.args) != 1 or len(ctx.args[0]) != 1: return ctx.default_signature - # - assert isinstance(ctx.api, TypeChecker) - inst_type = ctx.api.expr_checker.accept(ctx.args[0][0]) - # - proper_type = get_proper_type(inst_type) + proper_type = get_proper_type(ctx.api.get_expression_type(ctx.args[0][0])) # fields(Any) -> Any, fields(type[Any]) -> Any if ( @@ -1098,7 +1087,7 @@ def fields_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl inner = get_proper_type(proper_type.upper_bound) if isinstance(inner, Instance): # We need to work arg_types to compensate for the attrs stubs. - arg_types = [inst_type] + arg_types = [proper_type] cls = inner.type elif isinstance(proper_type, CallableType): cls = proper_type.type_object() diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index c81de675d808..ec5bce219dbd 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -887,7 +887,10 @@ plugins=/test-data/unit/plugins/descriptor.py # flags: --config-file tmp/mypy.ini def dynamic_signature(arg1: str) -> str: ... -reveal_type(dynamic_signature(1)) # N: Revealed type is "builtins.int" +a: int = 1 +reveal_type(dynamic_signature(a)) # N: Revealed type is "builtins.int" +b: bytes = b'foo' +reveal_type(dynamic_signature(b)) # N: Revealed type is "builtins.bytes" [file mypy.ini] \[mypy] plugins=/test-data/unit/plugins/function_sig_hook.py diff --git a/test-data/unit/plugins/function_sig_hook.py b/test-data/unit/plugins/function_sig_hook.py index d83c7df26209..4d901b96716e 100644 --- a/test-data/unit/plugins/function_sig_hook.py +++ b/test-data/unit/plugins/function_sig_hook.py @@ -1,5 +1,5 @@ -from mypy.plugin import CallableType, CheckerPluginInterface, FunctionSigContext, Plugin -from mypy.types import Instance, Type +from mypy.plugin import CallableType, FunctionSigContext, Plugin + class FunctionSigPlugin(Plugin): def get_function_signature_hook(self, fullname): @@ -7,20 +7,17 @@ def get_function_signature_hook(self, fullname): return my_hook return None -def _str_to_int(api: CheckerPluginInterface, typ: Type) -> Type: - if isinstance(typ, Instance): - if typ.type.fullname == 'builtins.str': - return api.named_generic_type('builtins.int', []) - elif typ.args: - return typ.copy_modified(args=[_str_to_int(api, t) for t in typ.args]) - - return typ def my_hook(ctx: FunctionSigContext) -> CallableType: + arg1_args = ctx.args[0] + if len(arg1_args) != 1: + return ctx.default_signature + arg1_type = ctx.api.get_expression_type(arg1_args[0]) return ctx.default_signature.copy_modified( - arg_types=[_str_to_int(ctx.api, t) for t in ctx.default_signature.arg_types], - ret_type=_str_to_int(ctx.api, ctx.default_signature.ret_type), + arg_types=[arg1_type], + ret_type=arg1_type, ) + def plugin(version): return FunctionSigPlugin