Skip to content

Commit 81b63cf

Browse files
Merge branch 'master' into meet_literal
2 parents 8dbc066 + cc5f1e1 commit 81b63cf

16 files changed

+543
-137
lines changed

mypy/checker.py

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
WhileStmt,
131131
WithStmt,
132132
YieldExpr,
133+
get_func_def,
133134
is_final_node,
134135
)
135136
from mypy.operators import flip_ops, int_op_to_method, neg_ops
@@ -430,6 +431,13 @@ def __init__(
430431
self._expr_checker = mypy.checkexpr.ExpressionChecker(
431432
self, self.msg, self.plugin, per_line_checking_time_ns
432433
)
434+
435+
self._str_type: Instance | None = None
436+
self._function_type: Instance | None = None
437+
self._int_type: Instance | None = None
438+
self._bool_type: Instance | None = None
439+
self._object_type: Instance | None = None
440+
433441
self.pattern_checker = PatternChecker(self, self.msg, self.plugin, options)
434442
self._unique_id = 0
435443

@@ -703,6 +711,12 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
703711
# TODO: keep precise type for callables with tricky but valid signatures.
704712
setter_type = fallback_setter_type
705713
defn.items[0].var.setter_type = setter_type
714+
if isinstance(defn.type, Overloaded):
715+
# Update legacy property type for decorated properties.
716+
getter_type = self.extract_callable_type(defn.items[0].var.type, defn)
717+
if getter_type is not None:
718+
getter_type.definition = defn.items[0]
719+
defn.type.items[0] = getter_type
706720
for i, fdef in enumerate(defn.items):
707721
assert isinstance(fdef, Decorator)
708722
if defn.is_property:
@@ -730,7 +744,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
730744
assert isinstance(item, Decorator)
731745
item_type = self.extract_callable_type(item.var.type, item)
732746
if item_type is not None:
733-
item_type.definition = item.func
747+
item_type.definition = item
734748
item_types.append(item_type)
735749
if item_types:
736750
defn.type = Overloaded(item_types)
@@ -2501,8 +2515,9 @@ def check_override(
25012515

25022516
override_ids = override.type_var_ids()
25032517
type_name = None
2504-
if isinstance(override.definition, FuncDef):
2505-
type_name = override.definition.info.name
2518+
definition = get_func_def(override)
2519+
if isinstance(definition, FuncDef):
2520+
type_name = definition.info.name
25062521

25072522
def erase_override(t: Type) -> Type:
25082523
return erase_typevars(t, ids_to_erase=override_ids)
@@ -3509,6 +3524,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
35093524
continue
35103525

35113526
base_type, base_node = self.node_type_from_base(lvalue_node.name, base, lvalue)
3527+
# TODO: if the r.h.s. is a descriptor, we should check setter override as well.
35123528
custom_setter = is_custom_settable_property(base_node)
35133529
if isinstance(base_type, PartialType):
35143530
base_type = None
@@ -4494,6 +4510,8 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
44944510
if isinstance(p_type, Overloaded):
44954511
# TODO: in theory we can have a property with a deleter only.
44964512
var.is_settable_property = True
4513+
assert isinstance(definition, Decorator), definition
4514+
var.setter_type = definition.var.setter_type
44974515

44984516
def set_inference_error_fallback_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
44994517
"""Store best known type for variable if type inference failed.
@@ -5356,6 +5374,8 @@ def visit_decorator_inner(
53565374
self.check_untyped_after_decorator(sig, e.func)
53575375
self.require_correct_self_argument(sig, e.func)
53585376
sig = set_callable_name(sig, e.func)
5377+
if isinstance(sig, CallableType):
5378+
sig.definition = e
53595379
e.var.type = sig
53605380
e.var.is_ready = True
53615381
if e.func.is_property:
@@ -7356,6 +7376,29 @@ def named_type(self, name: str) -> Instance:
73567376
73577377
For example, named_type('builtins.object') produces the 'object' type.
73587378
"""
7379+
if name == "builtins.str":
7380+
if self._str_type is None:
7381+
self._str_type = self._named_type(name)
7382+
return self._str_type
7383+
if name == "builtins.function":
7384+
if self._function_type is None:
7385+
self._function_type = self._named_type(name)
7386+
return self._function_type
7387+
if name == "builtins.int":
7388+
if self._int_type is None:
7389+
self._int_type = self._named_type(name)
7390+
return self._int_type
7391+
if name == "builtins.bool":
7392+
if self._bool_type is None:
7393+
self._bool_type = self._named_type(name)
7394+
return self._bool_type
7395+
if name == "builtins.object":
7396+
if self._object_type is None:
7397+
self._object_type = self._named_type(name)
7398+
return self._object_type
7399+
return self._named_type(name)
7400+
7401+
def _named_type(self, name: str) -> Instance:
73597402
# Assume that the name refers to a type.
73607403
sym = self.lookup_qualified(name)
73617404
node = sym.node
@@ -8654,17 +8697,21 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type:
86548697
return t.copy_modified(args=[a.accept(self) for a in t.args])
86558698

86568699

8657-
def is_classmethod_node(node: Node | None) -> bool | None:
8700+
def is_classmethod_node(node: SymbolNode | None) -> bool | None:
86588701
"""Find out if a node describes a classmethod."""
8702+
if isinstance(node, Decorator):
8703+
node = node.func
86598704
if isinstance(node, FuncDef):
86608705
return node.is_class
86618706
if isinstance(node, Var):
86628707
return node.is_classmethod
86638708
return None
86648709

86658710

8666-
def is_node_static(node: Node | None) -> bool | None:
8711+
def is_node_static(node: SymbolNode | None) -> bool | None:
86678712
"""Find out if a node describes a static function method."""
8713+
if isinstance(node, Decorator):
8714+
node = node.func
86688715
if isinstance(node, FuncDef):
86698716
return node.is_static
86708717
if isinstance(node, Var):

mypy/checkexpr.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ def __init__(
360360
] = {}
361361
self.in_lambda_expr = False
362362

363+
self._literal_true: Instance | None = None
364+
self._literal_false: Instance | None = None
365+
363366
def reset(self) -> None:
364367
self.resolved_type = {}
365368
self.expr_cache.clear()
@@ -3428,11 +3431,19 @@ def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Ty
34283431
if self.is_literal_context():
34293432
return LiteralType(value=value, fallback=typ)
34303433
else:
3431-
return typ.copy_modified(
3432-
last_known_value=LiteralType(
3433-
value=value, fallback=typ, line=typ.line, column=typ.column
3434-
)
3435-
)
3434+
if value is True:
3435+
if self._literal_true is None:
3436+
self._literal_true = typ.copy_modified(
3437+
last_known_value=LiteralType(value=value, fallback=typ)
3438+
)
3439+
return self._literal_true
3440+
if value is False:
3441+
if self._literal_false is None:
3442+
self._literal_false = typ.copy_modified(
3443+
last_known_value=LiteralType(value=value, fallback=typ)
3444+
)
3445+
return self._literal_false
3446+
return typ.copy_modified(last_known_value=LiteralType(value=value, fallback=typ))
34363447

34373448
def concat_tuples(self, left: TupleType, right: TupleType) -> TupleType:
34383449
"""Concatenate two fixed length tuples."""

mypy/checkmember.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -976,8 +976,15 @@ def expand_and_bind_callable(
976976
freeze_all_type_vars(expanded)
977977
if not var.is_property:
978978
return expanded
979-
# TODO: a decorated property can result in Overloaded here.
980-
assert isinstance(expanded, CallableType)
979+
if isinstance(expanded, Overloaded):
980+
# Legacy way to store settable properties is with overloads. Also in case it is
981+
# an actual overloaded property, selecting first item that passed check_self_arg()
982+
# is a good approximation, long-term we should use check_call() inference below.
983+
if not expanded.items:
984+
# A broken overload, error should be already reported.
985+
return AnyType(TypeOfAny.from_error)
986+
expanded = expanded.items[0]
987+
assert isinstance(expanded, CallableType), expanded
981988
if var.is_settable_property and mx.is_lvalue and var.setter_type is not None:
982989
if expanded.variables:
983990
type_ctx = mx.rvalue or TempNode(AnyType(TypeOfAny.special_form), context=mx.context)

mypy/fixup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,7 @@ def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None:
181181
if isinstance(o.type, Overloaded):
182182
# For error messages we link the original definition for each item.
183183
for typ, item in zip(o.type.items, o.items):
184-
if isinstance(item, Decorator):
185-
typ.definition = item.func
184+
typ.definition = item
186185

187186
def visit_decorator(self, d: Decorator) -> None:
188187
if self.current_info is not None:
@@ -193,8 +192,9 @@ def visit_decorator(self, d: Decorator) -> None:
193192
d.var.accept(self)
194193
for node in d.decorators:
195194
node.accept(self)
196-
if isinstance(d.var.type, ProperType) and isinstance(d.var.type, CallableType):
197-
d.var.type.definition = d.func
195+
typ = d.var.type
196+
if isinstance(typ, ProperType) and isinstance(typ, CallableType):
197+
typ.definition = d.func
198198

199199
def visit_class_def(self, c: ClassDef) -> None:
200200
for v in c.type_vars:

mypy/messages.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
SymbolTable,
5656
TypeInfo,
5757
Var,
58+
get_func_def,
5859
reverse_builtin_aliases,
5960
)
6061
from mypy.operators import op_methods, op_methods_to_symbols
@@ -2938,10 +2939,11 @@ def format_single(arg: Type) -> str:
29382939

29392940
def pretty_class_or_static_decorator(tp: CallableType) -> str | None:
29402941
"""Return @classmethod or @staticmethod, if any, for the given callable type."""
2941-
if tp.definition is not None and isinstance(tp.definition, SYMBOL_FUNCBASE_TYPES):
2942-
if tp.definition.is_class:
2942+
definition = get_func_def(tp)
2943+
if definition is not None and isinstance(definition, SYMBOL_FUNCBASE_TYPES):
2944+
if definition.is_class:
29432945
return "@classmethod"
2944-
if tp.definition.is_static:
2946+
if definition.is_static:
29452947
return "@staticmethod"
29462948
return None
29472949

@@ -2991,12 +2993,13 @@ def [T <: int] f(self, x: int, y: T) -> None
29912993
slash = True
29922994

29932995
# If we got a "special arg" (i.e: self, cls, etc...), prepend it to the arg list
2996+
definition = get_func_def(tp)
29942997
if (
2995-
isinstance(tp.definition, FuncDef)
2996-
and hasattr(tp.definition, "arguments")
2998+
isinstance(definition, FuncDef)
2999+
and hasattr(definition, "arguments")
29973000
and not tp.from_concatenate
29983001
):
2999-
definition_arg_names = [arg.variable.name for arg in tp.definition.arguments]
3002+
definition_arg_names = [arg.variable.name for arg in definition.arguments]
30003003
if (
30013004
len(definition_arg_names) > len(tp.arg_names)
30023005
and definition_arg_names[0]
@@ -3005,7 +3008,7 @@ def [T <: int] f(self, x: int, y: T) -> None
30053008
if s:
30063009
s = ", " + s
30073010
s = definition_arg_names[0] + s
3008-
s = f"{tp.definition.name}({s})"
3011+
s = f"{definition.name}({s})"
30093012
elif tp.name:
30103013
first_arg = get_first_arg(tp)
30113014
if first_arg:
@@ -3051,9 +3054,10 @@ def [T <: int] f(self, x: int, y: T) -> None
30513054

30523055

30533056
def get_first_arg(tp: CallableType) -> str | None:
3054-
if not isinstance(tp.definition, FuncDef) or not tp.definition.info or tp.definition.is_static:
3057+
definition = get_func_def(tp)
3058+
if not isinstance(definition, FuncDef) or not definition.info or definition.is_static:
30553059
return None
3056-
return tp.definition.original_first_arg
3060+
return definition.original_first_arg
30573061

30583062

30593063
def variance_string(variance: int) -> str:

mypy/nodes.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4380,6 +4380,13 @@ def is_final_node(node: SymbolNode | None) -> bool:
43804380
return isinstance(node, (Var, FuncDef, OverloadedFuncDef, Decorator)) and node.is_final
43814381

43824382

4383+
def get_func_def(typ: mypy.types.CallableType) -> SymbolNode | None:
4384+
definition = typ.definition
4385+
if isinstance(definition, Decorator):
4386+
definition = definition.func
4387+
return definition
4388+
4389+
43834390
def local_definitions(
43844391
names: SymbolTable, name_prefix: str, info: TypeInfo | None = None
43854392
) -> Iterator[Definition]:

mypy/semanal.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,10 @@ def __init__(
497497
# Used to track edge case when return is still inside except* if it enters a loop
498498
self.return_stmt_inside_except_star_block: bool = False
499499

500+
self._str_type: Instance | None = None
501+
self._function_type: Instance | None = None
502+
self._object_type: Instance | None = None
503+
500504
# mypyc doesn't properly handle implementing an abstractproperty
501505
# with a regular attribute so we make them properties
502506
@property
@@ -1241,8 +1245,9 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
12411245
# This is a property.
12421246
first_item.func.is_overload = True
12431247
bare_setter_type = self.analyze_property_with_multi_part_definition(defn)
1244-
typ = function_type(first_item.func, self.named_type("builtins.function"))
1248+
typ = function_type(first_item.func, self.function_type())
12451249
assert isinstance(typ, CallableType)
1250+
typ.definition = first_item
12461251
types = [typ]
12471252
else:
12481253
# This is a normal overload. Find the item signatures, the
@@ -1372,8 +1377,9 @@ def analyze_overload_sigs_and_impl(
13721377
item.accept(self)
13731378
# TODO: support decorated overloaded functions properly
13741379
if isinstance(item, Decorator):
1375-
callable = function_type(item.func, self.named_type("builtins.function"))
1380+
callable = function_type(item.func, self.function_type())
13761381
assert isinstance(callable, CallableType)
1382+
callable.definition = item
13771383
if not any(refers_to_fullname(dec, OVERLOAD_NAMES) for dec in item.decorators):
13781384
if i == len(defn.items) - 1 and not self.is_stub_file:
13791385
# Last item outside a stub is impl
@@ -1534,9 +1540,7 @@ def analyze_property_with_multi_part_definition(
15341540
if first_node.name == "setter":
15351541
# The first item represents the entire property.
15361542
first_item.var.is_settable_property = True
1537-
setter_func_type = function_type(
1538-
item.func, self.named_type("builtins.function")
1539-
)
1543+
setter_func_type = function_type(item.func, self.function_type())
15401544
assert isinstance(setter_func_type, CallableType)
15411545
bare_setter_type = setter_func_type
15421546
defn.setter_index = i + 1
@@ -6628,10 +6632,19 @@ def lookup_fully_qualified_or_none(self, fullname: str) -> SymbolTableNode | Non
66286632
return result
66296633

66306634
def object_type(self) -> Instance:
6631-
return self.named_type("builtins.object")
6635+
if self._object_type is None:
6636+
self._object_type = self.named_type("builtins.object")
6637+
return self._object_type
66326638

66336639
def str_type(self) -> Instance:
6634-
return self.named_type("builtins.str")
6640+
if self._str_type is None:
6641+
self._str_type = self.named_type("builtins.str")
6642+
return self._str_type
6643+
6644+
def function_type(self) -> Instance:
6645+
if self._function_type is None:
6646+
self._function_type = self.named_type("builtins.function")
6647+
return self._function_type
66356648

66366649
def named_type(self, fullname: str, args: list[Type] | None = None) -> Instance:
66376650
sym = self.lookup_fully_qualified(fullname)

mypy/server/astdiff.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,15 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb
252252
setter_type = snapshot_optional_type(first_item.var.setter_type)
253253
is_trivial_body = impl.is_trivial_body if impl else False
254254
dataclass_transform_spec = find_dataclass_transform_spec(node)
255+
256+
deprecated: str | list[str | None] | None = None
257+
if isinstance(node, FuncDef):
258+
deprecated = node.deprecated
259+
elif isinstance(node, OverloadedFuncDef):
260+
deprecated = [node.deprecated] + [
261+
i.func.deprecated for i in node.items if isinstance(i, Decorator)
262+
]
263+
255264
return (
256265
"Func",
257266
common,
@@ -262,7 +271,7 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb
262271
signature,
263272
is_trivial_body,
264273
dataclass_transform_spec.serialize() if dataclass_transform_spec is not None else None,
265-
node.deprecated if isinstance(node, FuncDef) else None,
274+
deprecated,
266275
setter_type, # multi-part properties are stored as OverloadedFuncDef
267276
)
268277
elif isinstance(node, Var):

mypy/typeops.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,15 +521,16 @@ def callable_corresponding_argument(
521521

522522
# def right(a: int = ...) -> None: ...
523523
# def left(__a: int = ..., *, a: int = ...) -> None: ...
524-
from mypy.subtypes import is_equivalent
524+
from mypy.meet import meet_types
525525

526526
if (
527527
not (by_name.required or by_pos.required)
528528
and by_pos.name is None
529529
and by_name.pos is None
530-
and is_equivalent(by_name.typ, by_pos.typ)
531530
):
532-
return FormalArgument(by_name.name, by_pos.pos, by_name.typ, False)
531+
return FormalArgument(
532+
by_name.name, by_pos.pos, meet_types(by_name.typ, by_pos.typ), False
533+
)
533534
return by_name if by_name is not None else by_pos
534535

535536

0 commit comments

Comments
 (0)