Skip to content

Commit 5bdfe7e

Browse files
authored
Respect force-union-syntax flag in error hint (#20165)
The error hint for `Need type annotations for ...` should defer to `options.use_union_syntax` when evaluating if `Union` or `|` should be used. This will also make it easier to eventually upgrade all tests to use the PEP 604 syntax.
1 parent 904fc77 commit 5bdfe7e

File tree

5 files changed

+12
-16
lines changed

5 files changed

+12
-16
lines changed

mypy/checker.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4449,7 +4449,7 @@ def infer_variable_type(
44494449
# partial type which will be made more specific later. A partial type
44504450
# gets generated in assignment like 'x = []' where item type is not known.
44514451
if name.name != "_" and not self.infer_partial_type(name, lvalue, init_type):
4452-
self.msg.need_annotation_for_var(name, context, self.options.python_version)
4452+
self.msg.need_annotation_for_var(name, context, self.options)
44534453
self.set_inference_error_fallback_type(name, lvalue, init_type)
44544454
elif (
44554455
isinstance(lvalue, MemberExpr)
@@ -4459,7 +4459,7 @@ def infer_variable_type(
44594459
and not is_same_type(self.inferred_attribute_types[lvalue.def_var], init_type)
44604460
):
44614461
# Multiple, inconsistent types inferred for an attribute.
4462-
self.msg.need_annotation_for_var(name, context, self.options.python_version)
4462+
self.msg.need_annotation_for_var(name, context, self.options)
44634463
name.type = AnyType(TypeOfAny.from_error)
44644464
else:
44654465
# Infer type of the target.
@@ -4656,9 +4656,7 @@ def check_simple_assignment(
46564656
rvalue, type_context=lvalue_type, always_allow_any=always_allow_any
46574657
)
46584658
if not is_valid_inferred_type(rvalue_type, self.options) and inferred is not None:
4659-
self.msg.need_annotation_for_var(
4660-
inferred, context, self.options.python_version
4661-
)
4659+
self.msg.need_annotation_for_var(inferred, context, self.options)
46624660
rvalue_type = rvalue_type.accept(SetNothingToAny())
46634661

46644662
if (
@@ -7680,7 +7678,7 @@ def enter_partial_types(
76807678
var.type = NoneType()
76817679
else:
76827680
if var not in self.partial_reported and not permissive:
7683-
self.msg.need_annotation_for_var(var, context, self.options.python_version)
7681+
self.msg.need_annotation_for_var(var, context, self.options)
76847682
self.partial_reported.add(var)
76857683
if var.type:
76867684
fixed = fixup_partial_type(var.type)
@@ -7707,9 +7705,7 @@ def handle_partial_var_type(
77077705
if in_scope:
77087706
context = partial_types[node]
77097707
if is_local or not self.options.allow_untyped_globals:
7710-
self.msg.need_annotation_for_var(
7711-
node, context, self.options.python_version
7712-
)
7708+
self.msg.need_annotation_for_var(node, context, self.options)
77137709
self.partial_reported.add(node)
77147710
else:
77157711
# Defer the node -- we might get a better type in the outer scope

mypy/messages.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,18 +1803,17 @@ def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> N
18031803
)
18041804

18051805
def need_annotation_for_var(
1806-
self, node: SymbolNode, context: Context, python_version: tuple[int, int] | None = None
1806+
self, node: SymbolNode, context: Context, options: Options | None = None
18071807
) -> None:
18081808
hint = ""
1809-
pep604_supported = not python_version or python_version >= (3, 10)
18101809
# type to recommend the user adds
18111810
recommended_type = None
18121811
# Only gives hint if it's a variable declaration and the partial type is a builtin type
1813-
if python_version and isinstance(node, Var) and isinstance(node.type, PartialType):
1812+
if options and isinstance(node, Var) and isinstance(node.type, PartialType):
18141813
type_dec = "<type>"
18151814
if not node.type.type:
18161815
# partial None
1817-
if pep604_supported:
1816+
if options.use_or_syntax():
18181817
recommended_type = f"{type_dec} | None"
18191818
else:
18201819
recommended_type = f"Optional[{type_dec}]"

mypy/suggestions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,8 @@ def visit_typeddict_type(self, t: TypedDictType) -> str:
890890

891891
def visit_union_type(self, t: UnionType) -> str:
892892
if len(t.items) == 2 and is_overlapping_none(t):
893-
return f"Optional[{remove_optional(t).accept(self)}]"
893+
s = remove_optional(t).accept(self)
894+
return f"{s} | None" if self.options.use_or_syntax() else f"Optional[{s}]"
894895
else:
895896
return super().visit_union_type(t)
896897

mypy/test/testcheck.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def run_case_once(
140140
options.hide_error_codes = False
141141
if "abstract" not in testcase.file:
142142
options.allow_empty_bodies = not testcase.name.endswith("_no_empty")
143-
if "union-error" not in testcase.file:
143+
if "union-error" not in testcase.file and "Pep604" not in testcase.name:
144144
options.force_union_syntax = True
145145

146146
if incremental_step and options.incremental:

test-data/unit/check-inference.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3548,7 +3548,7 @@ if x:
35483548
[builtins fixtures/dict.pyi]
35493549

35503550
[case testSuggestPep604AnnotationForPartialNone]
3551-
# flags: --local-partial-types --python-version 3.10
3551+
# flags: --local-partial-types --python-version 3.10 --no-force-union-syntax
35523552
x = None # E: Need type annotation for "x" (hint: "x: <type> | None = ...")
35533553

35543554
[case testTupleContextFromIterable]

0 commit comments

Comments
 (0)