Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Never in more messages, use ambiguous in join #17304

Merged
merged 2 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mypy/copytype.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def visit_none_type(self, t: NoneType) -> ProperType:
return self.copy_common(t, NoneType())

def visit_uninhabited_type(self, t: UninhabitedType) -> ProperType:
dup = UninhabitedType(t.is_noreturn)
dup = UninhabitedType()
dup.ambiguous = t.ambiguous
return self.copy_common(t, dup)

Expand Down
4 changes: 2 additions & 2 deletions mypy/join.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ def join_instances(self, t: Instance, s: Instance) -> ProperType:
# TODO: contravariant case should use meet but pass seen instances as
# an argument to keep track of recursive checks.
elif type_var.variance in (INVARIANT, CONTRAVARIANT):
if isinstance(ta_proper, UninhabitedType) and not ta_proper.is_noreturn:
if isinstance(ta_proper, UninhabitedType) and ta_proper.ambiguous:
new_type = sa
elif isinstance(sa_proper, UninhabitedType) and not sa_proper.is_noreturn:
elif isinstance(sa_proper, UninhabitedType) and sa_proper.ambiguous:
new_type = ta
elif not is_equivalent(ta, sa):
self.seen_instances.pop()
Expand Down
7 changes: 2 additions & 5 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -2426,7 +2426,7 @@ def quote_type_string(type_string: str) -> str:
"""Quotes a type representation for use in messages."""
no_quote_regex = r"^<(tuple|union): \d+ items>$"
if (
type_string in ["Module", "overloaded function", "Never", "<deleted>"]
type_string in ["Module", "overloaded function", "<deleted>"]
or type_string.startswith("Module ")
or re.match(no_quote_regex, type_string) is not None
or type_string.endswith("?")
Expand Down Expand Up @@ -2629,10 +2629,7 @@ def format_literal_value(typ: LiteralType) -> str:
elif isinstance(typ, DeletedType):
return "<deleted>"
elif isinstance(typ, UninhabitedType):
if typ.is_noreturn:
return "NoReturn"
else:
return "Never"
return "Never"
elif isinstance(typ, TypeType):
type_name = "type" if options.use_lowercase_names() else "Type"
return f"{type_name}[{format(typ.item)}]"
Expand Down
2 changes: 1 addition & 1 deletion mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
return AnyType(TypeOfAny.from_error)
return self.anal_type(t.args[0])
elif fullname in NEVER_NAMES:
return UninhabitedType(is_noreturn=True)
return UninhabitedType()
elif fullname in LITERAL_TYPE_NAMES:
return self.analyze_literal_type(t)
elif fullname in ANNOTATED_TYPE_NAMES:
Expand Down
13 changes: 4 additions & 9 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1169,17 +1169,12 @@ class UninhabitedType(ProperType):
is_subtype(UninhabitedType, T) = True
"""

__slots__ = ("ambiguous", "is_noreturn")
__slots__ = ("ambiguous",)

is_noreturn: bool # Does this come from a NoReturn? Purely for error messages.
# It is important to track whether this is an actual NoReturn type, or just a result
# of ambiguous type inference, in the latter case we don't want to mark a branch as
# unreachable in binder.
ambiguous: bool # Is this a result of inference for a variable without constraints?

def __init__(self, is_noreturn: bool = False, line: int = -1, column: int = -1) -> None:
def __init__(self, line: int = -1, column: int = -1) -> None:
super().__init__(line, column)
self.is_noreturn = is_noreturn
self.ambiguous = False

def can_be_true_default(self) -> bool:
Expand All @@ -1198,12 +1193,12 @@ def __eq__(self, other: object) -> bool:
return isinstance(other, UninhabitedType)

def serialize(self) -> JsonDict:
return {".class": "UninhabitedType", "is_noreturn": self.is_noreturn}
return {".class": "UninhabitedType"}

@classmethod
def deserialize(cls, data: JsonDict) -> UninhabitedType:
assert data[".class"] == "UninhabitedType"
return UninhabitedType(is_noreturn=data["is_noreturn"])
return UninhabitedType()


class NoneType(ProperType):
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-dataclasses.test
Original file line number Diff line number Diff line change
Expand Up @@ -2080,8 +2080,8 @@ class B:
a_or_b: Union[A[int], B]
_ = replace(a_or_b, x=42, y=True, init_var=42)
_ = replace(a_or_b, x=42, y=True) # E: Missing named argument "init_var" for "replace" of "Union[A[int], B]"
_ = replace(a_or_b, x=42, y=True, z='42', init_var=42) # E: Argument "z" to "replace" of "Union[A[int], B]" has incompatible type "str"; expected Never
_ = replace(a_or_b, x=42, y=True, w={}, init_var=42) # E: Argument "w" to "replace" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected Never
_ = replace(a_or_b, x=42, y=True, z='42', init_var=42) # E: Argument "z" to "replace" of "Union[A[int], B]" has incompatible type "str"; expected "Never"
_ = replace(a_or_b, x=42, y=True, w={}, init_var=42) # E: Argument "w" to "replace" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected "Never"
_ = replace(a_or_b, y=42, init_var=42) # E: Argument "y" to "replace" of "Union[A[int], B]" has incompatible type "int"; expected "bool"

[builtins fixtures/tuple.pyi]
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-flags.test
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ reveal_type(f() or no_return()) # N: Revealed type is "builtins.int"
# flags: --warn-no-return
from mypy_extensions import NoReturn

x = 0 # type: NoReturn # E: Incompatible types in assignment (expression has type "int", variable has type "NoReturn")
x = 0 # type: NoReturn # E: Incompatible types in assignment (expression has type "int", variable has type "Never")
[builtins fixtures/dict.pyi]

[case testNoReturnAsync]
Expand Down Expand Up @@ -477,7 +477,7 @@ def no_return() -> NoReturn: pass
def f() -> NoReturn:
no_return()

x: NoReturn = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "NoReturn")
x: NoReturn = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "Never")
[builtins fixtures/dict.pyi]

[case testShowErrorContextFunction]
Expand Down
6 changes: 2 additions & 4 deletions test-data/unit/check-literal.test
Original file line number Diff line number Diff line change
Expand Up @@ -839,14 +839,13 @@ b: NoReturn
c: None

fa(lit)
fb(lit) # E: Argument 1 to "fb" has incompatible type "Literal[1]"; expected "NoReturn"
fb(lit) # E: Argument 1 to "fb" has incompatible type "Literal[1]"; expected "Never"
fc(lit) # E: Argument 1 to "fc" has incompatible type "Literal[1]"; expected "None"

f_lit(a)
f_lit(b)
f_lit(c) # E: Argument 1 to "f_lit" has incompatible type "None"; expected "Literal[1]"
[builtins fixtures/tuple.pyi]
[out]

[case testLiteralCheckSubtypingNoStrictOptional]
# flags: --no-strict-optional
Expand All @@ -865,14 +864,13 @@ b: NoReturn
c: None

fa(lit)
fb(lit) # E: Argument 1 to "fb" has incompatible type "Literal[1]"; expected "NoReturn"
fb(lit) # E: Argument 1 to "fb" has incompatible type "Literal[1]"; expected "Never"
fc(lit) # E: Argument 1 to "fc" has incompatible type "Literal[1]"; expected "None"

f_lit(a)
f_lit(b)
f_lit(c)
[builtins fixtures/tuple.pyi]
[out]

[case testLiteralCallingOverloadedFunction]
from typing import overload, Generic, TypeVar, Any
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-plugin-attrs.test
Original file line number Diff line number Diff line change
Expand Up @@ -2170,8 +2170,8 @@ class B:

a_or_b: A[int] | B
a2 = attrs.evolve(a_or_b, x=42, y=True)
a2 = attrs.evolve(a_or_b, x=42, y=True, z='42') # E: Argument "z" to "evolve" of "Union[A[int], B]" has incompatible type "str"; expected Never
a2 = attrs.evolve(a_or_b, x=42, y=True, w={}) # E: Argument "w" to "evolve" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected Never
a2 = attrs.evolve(a_or_b, x=42, y=True, z='42') # E: Argument "z" to "evolve" of "Union[A[int], B]" has incompatible type "str"; expected "Never"
a2 = attrs.evolve(a_or_b, x=42, y=True, w={}) # E: Argument "w" to "evolve" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected "Never"

[builtins fixtures/plugin_attrs.pyi]

Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-python310.test
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,7 @@ def f(value: int) -> int: # E: Missing return statement
case 2:
return 1
case o:
assert_never(o) # E: Argument 1 to "assert_never" has incompatible type "int"; expected "NoReturn"
assert_never(o) # E: Argument 1 to "assert_never" has incompatible type "int"; expected "Never"

[case testMatchExhaustiveNoError]
from typing import NoReturn, Union, Literal
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-type-aliases.test
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Never = NoReturn
a: Never # Used to be an error here

def f(a: Never): ...
f(5) # E: Argument 1 to "f" has incompatible type "int"; expected "NoReturn"
f(5) # E: Argument 1 to "f" has incompatible type "int"; expected "Never"
[case testImportUnionAlias]
import typing
from _m import U
Expand Down
6 changes: 3 additions & 3 deletions test-data/unit/check-typeddict.test
Original file line number Diff line number Diff line change
Expand Up @@ -1648,7 +1648,7 @@ a.setdefault('y', '') # E: Argument 2 to "setdefault" of "TypedDict" has incompa
x = ''
a.setdefault(x, 1) # E: Expected TypedDict key to be string literal
alias = a.setdefault
alias(x, 1) # E: Argument 1 has incompatible type "str"; expected "NoReturn"
alias(x, 1) # E: Argument 1 has incompatible type "str"; expected "Never"

a.update({})
a.update({'x': 1})
Expand Down Expand Up @@ -1680,8 +1680,8 @@ b.pop('x') # E: Key "x" of TypedDict "B" cannot be deleted
x = ''
b.pop(x) # E: Expected TypedDict key to be string literal
pop = b.pop
pop('x') # E: Argument 1 has incompatible type "str"; expected "NoReturn"
pop('invalid') # E: Argument 1 has incompatible type "str"; expected "NoReturn"
pop('x') # E: Argument 1 has incompatible type "str"; expected "Never"
pop('invalid') # E: Argument 1 has incompatible type "str"; expected "Never"
[builtins fixtures/dict.pyi]

[case testTypedDictDel]
Expand Down
Loading