Skip to content

Commit 43476aa

Browse files
authored
Cleaner usage of get_proper_type() (#13326)
This is a follow up for #13297 I move around some calls to `get_proper_type()` to preserve original types as much as possible (plus few related required low-risk changes). This makes error messages much more concise, before some error messages in the tests I added were truly epic: ``` Incompatible types in assignment (expression has type "Sequence[Union[B, Sequence[Union[B, Sequence[Union[B, Sequence[Union[B, Sequence[Union[B, Sequence[Union[B, NestedB]]]]]]]]]]]]", variable has type "int") ```
1 parent 7251ef8 commit 43476aa

16 files changed

+211
-108
lines changed

misc/proper_plugin.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ def is_special_target(right: ProperType) -> bool:
8484
return True
8585
if right.type_object().fullname in (
8686
"mypy.types.UnboundType",
87+
"mypy.types.TypeVarLikeType",
8788
"mypy.types.TypeVarType",
89+
"mypy.types.UnpackType",
90+
"mypy.types.TypeVarTupleType",
8891
"mypy.types.ParamSpecType",
8992
"mypy.types.RawExpressionType",
9093
"mypy.types.EllipsisType",
@@ -93,6 +96,7 @@ def is_special_target(right: ProperType) -> bool:
9396
"mypy.types.CallableArgument",
9497
"mypy.types.PartialType",
9598
"mypy.types.ErasedType",
99+
"mypy.types.DeletedType",
96100
):
97101
# Special case: these are not valid targets for a type alias and thus safe.
98102
# TODO: introduce a SyntheticType base to simplify this?

mypy/applytype.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,18 @@
99
Parameters,
1010
ParamSpecType,
1111
PartialType,
12-
ProperType,
1312
Type,
1413
TypeVarId,
1514
TypeVarLikeType,
1615
TypeVarTupleType,
1716
TypeVarType,
1817
get_proper_type,
19-
get_proper_types,
2018
)
2119

2220

2321
def get_target_type(
2422
tvar: TypeVarLikeType,
25-
type: ProperType,
23+
type: Type,
2624
callable: CallableType,
2725
report_incompatible_typevar_value: Callable[[CallableType, Type, str, Context], None],
2826
context: Context,
@@ -33,14 +31,15 @@ def get_target_type(
3331
if isinstance(tvar, TypeVarTupleType):
3432
return type
3533
assert isinstance(tvar, TypeVarType)
36-
values = get_proper_types(tvar.values)
34+
values = tvar.values
35+
p_type = get_proper_type(type)
3736
if values:
38-
if isinstance(type, AnyType):
37+
if isinstance(p_type, AnyType):
3938
return type
40-
if isinstance(type, TypeVarType) and type.values:
39+
if isinstance(p_type, TypeVarType) and p_type.values:
4140
# Allow substituting T1 for T if every allowed value of T1
4241
# is also a legal value of T.
43-
if all(any(mypy.subtypes.is_same_type(v, v1) for v in values) for v1 in type.values):
42+
if all(any(mypy.subtypes.is_same_type(v, v1) for v in values) for v1 in p_type.values):
4443
return type
4544
matching = []
4645
for value in values:
@@ -86,12 +85,10 @@ def apply_generic_arguments(
8685
assert len(tvars) == len(orig_types)
8786
# Check that inferred type variable values are compatible with allowed
8887
# values and bounds. Also, promote subtype values to allowed values.
89-
types = get_proper_types(orig_types)
90-
9188
# Create a map from type variable id to target type.
9289
id_to_type: Dict[TypeVarId, Type] = {}
9390

94-
for tvar, type in zip(tvars, types):
91+
for tvar, type in zip(tvars, orig_types):
9592
assert not isinstance(type, PartialType), "Internal error: must never apply partial type"
9693
if type is None:
9794
continue

mypy/argmap.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ def expand_actual_type(
184184
This is supposed to be called for each formal, in order. Call multiple times per
185185
formal if multiple actuals map to a formal.
186186
"""
187+
original_actual = actual_type
187188
actual_type = get_proper_type(actual_type)
188189
if actual_kind == nodes.ARG_STAR:
189190
if isinstance(actual_type, Instance) and actual_type.args:
@@ -241,4 +242,4 @@ def expand_actual_type(
241242
return AnyType(TypeOfAny.from_error)
242243
else:
243244
# No translation for other kinds -- 1:1 mapping.
244-
return actual_type
245+
return original_actual

mypy/binder.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,6 @@ def assign_type(
260260
# it means that the target is not final, and therefore can't hold a literal.
261261
type = remove_instance_last_known_values(type)
262262

263-
type = get_proper_type(type)
264-
declared_type = get_proper_type(declared_type)
265-
266263
if self.type_assignments is not None:
267264
# We are in a multiassign from union, defer the actual binding,
268265
# just collect the types.
@@ -287,6 +284,8 @@ def assign_type(
287284
# times?
288285
return
289286

287+
p_declared = get_proper_type(declared_type)
288+
p_type = get_proper_type(type)
290289
enclosing_type = get_proper_type(self.most_recent_enclosing_type(expr, type))
291290
if isinstance(enclosing_type, AnyType) and not restrict_any:
292291
# If x is Any and y is int, after x = y we do not infer that x is int.
@@ -302,22 +301,22 @@ def assign_type(
302301
# in order to prevent false positives.
303302
# (See discussion in #3526)
304303
elif (
305-
isinstance(type, AnyType)
306-
and isinstance(declared_type, UnionType)
307-
and any(isinstance(get_proper_type(item), NoneType) for item in declared_type.items)
304+
isinstance(p_type, AnyType)
305+
and isinstance(p_declared, UnionType)
306+
and any(isinstance(get_proper_type(item), NoneType) for item in p_declared.items)
308307
and isinstance(
309308
get_proper_type(self.most_recent_enclosing_type(expr, NoneType())), NoneType
310309
)
311310
):
312311
# Replace any Nones in the union type with Any
313312
new_items = [
314313
type if isinstance(get_proper_type(item), NoneType) else item
315-
for item in declared_type.items
314+
for item in p_declared.items
316315
]
317316
self.put(expr, UnionType(new_items))
318-
elif isinstance(type, AnyType) and not (
319-
isinstance(declared_type, UnionType)
320-
and any(isinstance(get_proper_type(item), AnyType) for item in declared_type.items)
317+
elif isinstance(p_type, AnyType) and not (
318+
isinstance(p_declared, UnionType)
319+
and any(isinstance(get_proper_type(item), AnyType) for item in p_declared.items)
321320
):
322321
# Assigning an Any value doesn't affect the type to avoid false negatives, unless
323322
# there is an Any item in a declared union type.
@@ -444,7 +443,7 @@ def top_frame_context(self) -> Iterator[Frame]:
444443

445444
def get_declaration(expr: BindableExpression) -> Optional[Type]:
446445
if isinstance(expr, RefExpr) and isinstance(expr.node, Var):
447-
type = get_proper_type(expr.node.type)
448-
if not isinstance(type, PartialType):
446+
type = expr.node.type
447+
if not isinstance(get_proper_type(type), PartialType):
449448
return type
450449
return None

mypy/checker.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2619,20 +2619,22 @@ def check_assignment(
26192619

26202620
# Special case: only non-abstract non-protocol classes can be assigned to
26212621
# variables with explicit type Type[A], where A is protocol or abstract.
2622-
rvalue_type = get_proper_type(rvalue_type)
2623-
lvalue_type = get_proper_type(lvalue_type)
2622+
p_rvalue_type = get_proper_type(rvalue_type)
2623+
p_lvalue_type = get_proper_type(lvalue_type)
26242624
if (
2625-
isinstance(rvalue_type, CallableType)
2626-
and rvalue_type.is_type_obj()
2625+
isinstance(p_rvalue_type, CallableType)
2626+
and p_rvalue_type.is_type_obj()
26272627
and (
2628-
rvalue_type.type_object().is_abstract
2629-
or rvalue_type.type_object().is_protocol
2628+
p_rvalue_type.type_object().is_abstract
2629+
or p_rvalue_type.type_object().is_protocol
2630+
)
2631+
and isinstance(p_lvalue_type, TypeType)
2632+
and isinstance(p_lvalue_type.item, Instance)
2633+
and (
2634+
p_lvalue_type.item.type.is_abstract or p_lvalue_type.item.type.is_protocol
26302635
)
2631-
and isinstance(lvalue_type, TypeType)
2632-
and isinstance(lvalue_type.item, Instance)
2633-
and (lvalue_type.item.type.is_abstract or lvalue_type.item.type.is_protocol)
26342636
):
2635-
self.msg.concrete_only_assign(lvalue_type, rvalue)
2637+
self.msg.concrete_only_assign(p_lvalue_type, rvalue)
26362638
return
26372639
if rvalue_type and infer_lvalue_type and not isinstance(lvalue_type, PartialType):
26382640
# Don't use type binder for definitions of special forms, like named tuples.
@@ -3474,7 +3476,6 @@ def infer_variable_type(
34743476
self, name: Var, lvalue: Lvalue, init_type: Type, context: Context
34753477
) -> None:
34763478
"""Infer the type of initialized variables from initializer type."""
3477-
init_type = get_proper_type(init_type)
34783479
if isinstance(init_type, DeletedType):
34793480
self.msg.deleted_as_rvalue(init_type, context)
34803481
elif not is_valid_inferred_type(init_type) and not self.no_partial_types:
@@ -3620,23 +3621,21 @@ def check_simple_assignment(
36203621
# '...' is always a valid initializer in a stub.
36213622
return AnyType(TypeOfAny.special_form)
36223623
else:
3623-
orig_lvalue = lvalue_type
3624-
lvalue_type = get_proper_type(lvalue_type)
3625-
always_allow_any = lvalue_type is not None and not isinstance(lvalue_type, AnyType)
3624+
always_allow_any = lvalue_type is not None and not isinstance(
3625+
get_proper_type(lvalue_type), AnyType
3626+
)
36263627
rvalue_type = self.expr_checker.accept(
36273628
rvalue, lvalue_type, always_allow_any=always_allow_any
36283629
)
3629-
orig_rvalue = rvalue_type
3630-
rvalue_type = get_proper_type(rvalue_type)
36313630
if isinstance(rvalue_type, DeletedType):
36323631
self.msg.deleted_as_rvalue(rvalue_type, context)
36333632
if isinstance(lvalue_type, DeletedType):
36343633
self.msg.deleted_as_lvalue(lvalue_type, context)
36353634
elif lvalue_type:
36363635
self.check_subtype(
36373636
# Preserve original aliases for error messages when possible.
3638-
orig_rvalue,
3639-
orig_lvalue or lvalue_type,
3637+
rvalue_type,
3638+
lvalue_type,
36403639
context,
36413640
msg,
36423641
f"{rvalue_name} has type",

mypy/checkexpr.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3351,13 +3351,14 @@ def visit_index_expr(self, e: IndexExpr) -> Type:
33513351
It may also represent type application.
33523352
"""
33533353
result = self.visit_index_expr_helper(e)
3354-
result = get_proper_type(self.narrow_type_from_binder(e, result))
3354+
result = self.narrow_type_from_binder(e, result)
3355+
p_result = get_proper_type(result)
33553356
if (
33563357
self.is_literal_context()
3357-
and isinstance(result, Instance)
3358-
and result.last_known_value is not None
3358+
and isinstance(p_result, Instance)
3359+
and p_result.last_known_value is not None
33593360
):
3360-
result = result.last_known_value
3361+
result = p_result.last_known_value
33613362
return result
33623363

33633364
def visit_index_expr_helper(self, e: IndexExpr) -> Type:

mypy/erasetype.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,7 @@ class LastKnownValueEraser(TypeTranslator):
190190
def visit_instance(self, t: Instance) -> Type:
191191
if not t.last_known_value and not t.args:
192192
return t
193-
new_t = t.copy_modified(args=[a.accept(self) for a in t.args], last_known_value=None)
194-
new_t.can_be_true = t.can_be_true
195-
new_t.can_be_false = t.can_be_false
196-
return new_t
193+
return t.copy_modified(args=[a.accept(self) for a in t.args], last_known_value=None)
197194

198195
def visit_type_alias_type(self, t: TypeAliasType) -> Type:
199196
# Type aliases can't contain literal values, because they are
@@ -210,12 +207,14 @@ def visit_union_type(self, t: UnionType) -> Type:
210207
# Avoid merge in simple cases such as optional types.
211208
if len(instances) > 1:
212209
instances_by_name: Dict[str, List[Instance]] = {}
213-
new_items = get_proper_types(new.items)
214-
for item in new_items:
215-
if isinstance(item, Instance) and not item.args:
216-
instances_by_name.setdefault(item.type.fullname, []).append(item)
210+
p_new_items = get_proper_types(new.items)
211+
for p_item in p_new_items:
212+
if isinstance(p_item, Instance) and not p_item.args:
213+
instances_by_name.setdefault(p_item.type.fullname, []).append(p_item)
217214
merged: List[Type] = []
218-
for item in new_items:
215+
for item in new.items:
216+
orig_item = item
217+
item = get_proper_type(item)
219218
if isinstance(item, Instance) and not item.args:
220219
types = instances_by_name.get(item.type.fullname)
221220
if types is not None:
@@ -227,6 +226,6 @@ def visit_union_type(self, t: UnionType) -> Type:
227226
merged.append(make_simplified_union(types))
228227
del instances_by_name[item.type.fullname]
229228
else:
230-
merged.append(item)
229+
merged.append(orig_item)
231230
return UnionType.make_union(merged)
232231
return new

mypy/expandtype.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -139,21 +139,19 @@ def visit_instance(self, t: Instance) -> Type:
139139
return args
140140

141141
def visit_type_var(self, t: TypeVarType) -> Type:
142-
repl = get_proper_type(self.variables.get(t.id, t))
143-
if isinstance(repl, Instance):
144-
inst = repl
145-
return Instance(inst.type, inst.args, line=inst.line, column=inst.column)
146-
else:
147-
return repl
142+
repl = self.variables.get(t.id, t)
143+
if isinstance(repl, ProperType) and isinstance(repl, Instance):
144+
# TODO: do we really need to do this?
145+
# If I try to remove this special-casing ~40 tests fail on reveal_type().
146+
return repl.copy_modified(last_known_value=None)
147+
return repl
148148

149149
def visit_param_spec(self, t: ParamSpecType) -> Type:
150150
repl = get_proper_type(self.variables.get(t.id, t))
151151
if isinstance(repl, Instance):
152-
inst = repl
153-
# Return copy of instance with type erasure flag on.
154152
# TODO: what does prefix mean in this case?
155153
# TODO: why does this case even happen? Instances aren't plural.
156-
return Instance(inst.type, inst.args, line=inst.line, column=inst.column)
154+
return repl
157155
elif isinstance(repl, ParamSpecType):
158156
return repl.copy_modified(
159157
flavor=t.flavor,
@@ -199,9 +197,8 @@ def expand_unpack(self, t: UnpackType) -> Optional[Union[List[Type], Instance, A
199197
"""May return either a list of types to unpack to, any, or a single
200198
variable length tuple. The latter may not be valid in all contexts.
201199
"""
202-
proper_typ = get_proper_type(t.type)
203-
if isinstance(proper_typ, TypeVarTupleType):
204-
repl = get_proper_type(self.variables.get(proper_typ.id, t))
200+
if isinstance(t.type, TypeVarTupleType):
201+
repl = get_proper_type(self.variables.get(t.type.id, t))
205202
if isinstance(repl, TupleType):
206203
return repl.items
207204
if isinstance(repl, TypeList):
@@ -221,7 +218,7 @@ def expand_unpack(self, t: UnpackType) -> Optional[Union[List[Type], Instance, A
221218
else:
222219
raise NotImplementedError(f"Invalid type replacement to expand: {repl}")
223220
else:
224-
raise NotImplementedError(f"Invalid type to expand: {proper_typ}")
221+
raise NotImplementedError(f"Invalid type to expand: {t.type}")
225222

226223
def visit_parameters(self, t: Parameters) -> Type:
227224
return t.copy_modified(arg_types=self.expand_types(t.arg_types))
@@ -277,9 +274,8 @@ def expand_types_with_unpack(
277274
"""
278275
items: List[Type] = []
279276
for item in typs:
280-
proper_item = get_proper_type(item)
281-
if isinstance(proper_item, UnpackType):
282-
unpacked_items = self.expand_unpack(proper_item)
277+
if isinstance(item, UnpackType):
278+
unpacked_items = self.expand_unpack(item)
283279
if unpacked_items is None:
284280
# TODO: better error, something like tuple of unknown?
285281
return UninhabitedType()

0 commit comments

Comments
 (0)