diff --git a/conformance/results/mypy/overloads_basic.toml b/conformance/results/mypy/overloads_basic.toml index c42562c9..f592ea60 100644 --- a/conformance/results/mypy/overloads_basic.toml +++ b/conformance/results/mypy/overloads_basic.toml @@ -1,11 +1,9 @@ conformant = "Pass" output = """ -overloads_basic.py:37: error: No overload variant of "__getitem__" of "Bytes" matches argument type "str" [call-overload] -overloads_basic.py:37: note: Possible overload variants: -overloads_basic.py:37: note: def __getitem__(self, int, /) -> int -overloads_basic.py:37: note: def __getitem__(self, slice[Any, Any, Any], /) -> bytes -overloads_basic.py:62: error: Single overload definition, multiple required [misc] -overloads_basic.py:74: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_basic.py:39: error: No overload variant of "__getitem__" of "Bytes" matches argument type "str" [call-overload] +overloads_basic.py:39: note: Possible overload variants: +overloads_basic.py:39: note: def __getitem__(self, int, /) -> int +overloads_basic.py:39: note: def __getitem__(self, slice[Any, Any, Any], /) -> bytes """ conformance_automated = "Pass" errors_diff = """ diff --git a/conformance/results/mypy/overloads_consistency.toml b/conformance/results/mypy/overloads_consistency.toml new file mode 100644 index 00000000..6c02c196 --- /dev/null +++ b/conformance/results/mypy/overloads_consistency.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_consistency.py:27: error: Overloaded function implementation cannot produce return type of signature 2 [misc] +overloads_consistency.py:43: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] +""" diff --git a/conformance/results/mypy/overloads_definitions.toml b/conformance/results/mypy/overloads_definitions.toml new file mode 100644 index 00000000..d38c913f --- /dev/null +++ b/conformance/results/mypy/overloads_definitions.toml @@ -0,0 +1,24 @@ +conformant = "Partial" +conformance_automated = "Fail" +notes = """ +Does not allow an overload with no implementation in an abstract base class. +Allows @override to be on all overloads and implementation, instead of just implementation. +""" +errors_diff = """ +Line 245: Expected 1 errors +Line 49: Unexpected errors ['overloads_definitions.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]'] +""" +output = """ +overloads_definitions.py:14: error: Single overload definition, multiple required [misc] +overloads_definitions.py:26: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_definitions.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_definitions.py:63: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_definitions.py:78: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] +overloads_definitions.py:88: error: Overloaded function implementation does not accept all possible arguments of signature 1 [misc] +overloads_definitions.py:88: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] +overloads_definitions.py:91: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +overloads_definitions.py:133: error: @final should be applied only to overload implementation [misc] +overloads_definitions.py:148: error: @final should be applied only to overload implementation [misc] +overloads_definitions.py:196: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] +overloads_definitions.py:211: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] +""" diff --git a/conformance/results/mypy/overloads_definitions_stub.toml b/conformance/results/mypy/overloads_definitions_stub.toml new file mode 100644 index 00000000..d1623d20 --- /dev/null +++ b/conformance/results/mypy/overloads_definitions_stub.toml @@ -0,0 +1,18 @@ +conformant = "Partial" +notes = """ +Allows @override to appear in a stub file not on the first overload. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 168: Expected 1 errors +""" +output = """ +overloads_definitions_stub.pyi:13: error: Single overload definition, multiple required [misc] +overloads_definitions_stub.pyi:37: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] +overloads_definitions_stub.pyi:38: error: Self argument missing for a non-static method (or an invalid type for self) [misc] +overloads_definitions_stub.pyi:46: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +overloads_definitions_stub.pyi:85: error: In a stub file @final must be applied only to the first overload [misc] +overloads_definitions_stub.pyi:101: error: In a stub file @final must be applied only to the first overload [misc] +overloads_definitions_stub.pyi:128: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] +overloads_definitions_stub.pyi:140: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] +""" diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml new file mode 100644 index 00000000..b1d7d783 --- /dev/null +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -0,0 +1,43 @@ +conformant = "Partial" +notes = """ +Does not expand boolean arguments to Literal[True] and Literal[False]. +Does not expand enum arguments to literal variants. +Does not expand tuple arguments to possible combinations. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 106: Unexpected errors ['overloads_evaluation.py:106: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload]'] +Line 107: Unexpected errors ['overloads_evaluation.py:107: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] +Line 129: Unexpected errors ['overloads_evaluation.py:129: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload]'] +Line 130: Unexpected errors ['overloads_evaluation.py:130: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] +Line 169: Unexpected errors ['overloads_evaluation.py:169: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type]'] +Line 170: Unexpected errors ['overloads_evaluation.py:170: error: Expression is of type "int", not "int | str" [assert-type]'] +""" +output = """ +overloads_evaluation.py:32: error: All overload variants of "example1" require at least one argument [call-overload] +overloads_evaluation.py:32: note: Possible overload variants: +overloads_evaluation.py:32: note: def example1(x: int, y: str) -> int +overloads_evaluation.py:32: note: def example1(x: str) -> str +overloads_evaluation.py:40: error: No overload variant of "example1" matches argument types "int", "int" [call-overload] +overloads_evaluation.py:40: note: Possible overload variants: +overloads_evaluation.py:40: note: def example1(x: int, y: str) -> int +overloads_evaluation.py:40: note: def example1(x: str) -> str +overloads_evaluation.py:45: error: No overload variant of "example1" matches argument type "int" [call-overload] +overloads_evaluation.py:45: note: Possible overload variants: +overloads_evaluation.py:45: note: def example1(x: int, y: str) -> int +overloads_evaluation.py:45: note: def example1(x: str) -> str +overloads_evaluation.py:89: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] +overloads_evaluation.py:89: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] +overloads_evaluation.py:106: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload] +overloads_evaluation.py:106: note: Possible overload variants: +overloads_evaluation.py:106: note: def expand_bool(x: Literal[False]) -> Literal[0] +overloads_evaluation.py:106: note: def expand_bool(x: Literal[True]) -> Literal[1] +overloads_evaluation.py:107: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] +overloads_evaluation.py:129: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload] +overloads_evaluation.py:129: note: Possible overload variants: +overloads_evaluation.py:129: note: def expand_enum(x: Literal[Color.RED]) -> Literal[0] +overloads_evaluation.py:129: note: def expand_enum(x: Literal[Color.BLUE]) -> Literal[1] +overloads_evaluation.py:130: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] +overloads_evaluation.py:169: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type] +overloads_evaluation.py:170: error: Expression is of type "int", not "int | str" [assert-type] +""" diff --git a/conformance/results/mypy/overloads_overlap.toml b/conformance/results/mypy/overloads_overlap.toml new file mode 100644 index 00000000..dd72a284 --- /dev/null +++ b/conformance/results/mypy/overloads_overlap.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_overlap.py:14: error: Overloaded function signatures 1 and 2 overlap with incompatible return types [overload-overlap] +overloads_overlap.py:36: error: Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader [overload-cannot-match] +""" diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml index 3d498066..1012f096 100644 --- a/conformance/results/mypy/version.toml +++ b/conformance/results/mypy/version.toml @@ -1,2 +1,2 @@ -version = "mypy 1.14.0" -test_duration = 1.6 +version = "mypy 1.14.1" +test_duration = 1.7 diff --git a/conformance/results/pyre/overloads_basic.toml b/conformance/results/pyre/overloads_basic.toml index 7a5d5b54..aebf2ded 100644 --- a/conformance/results/pyre/overloads_basic.toml +++ b/conformance/results/pyre/overloads_basic.toml @@ -1,10 +1,6 @@ conformant = "Pass" -notes = """ -""" output = """ -overloads_basic.py:37:2 Incompatible parameter type [6]: In call `Bytes.__getitem__`, for 1st positional argument, expected `int` but got `str`. -overloads_basic.py:63:0 Incompatible overload [43]: At least two overload signatures must be present. -overloads_basic.py:75:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation. +overloads_basic.py:39:2 Incompatible parameter type [6]: In call `Bytes.__getitem__`, for 1st positional argument, expected `int` but got `str`. """ conformance_automated = "Pass" errors_diff = """ diff --git a/conformance/results/pyre/overloads_consistency.toml b/conformance/results/pyre/overloads_consistency.toml new file mode 100644 index 00000000..eb38a818 --- /dev/null +++ b/conformance/results/pyre/overloads_consistency.toml @@ -0,0 +1,18 @@ +conformant = "Partial" +notes = """ +Does not apply decorator transforms before checking overload consistency. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 107: Unexpected errors ['overloads_consistency.py:107:0 Incompatible overload [43]: The return type of overloaded function `decorated` (`None`) is incompatible with the return type of the implementation (`bytes`).', 'overloads_consistency.py:107:0 Incompatible overload [43]: The implementation of `decorated` does not accept all possible arguments of overload defined on line `107`.'] +Line 111: Unexpected errors ['overloads_consistency.py:111:0 Incompatible overload [43]: The return type of overloaded function `decorated` (`str`) is incompatible with the return type of the implementation (`bytes`).', 'overloads_consistency.py:111:0 Incompatible overload [43]: The implementation of `decorated` does not accept all possible arguments of overload defined on line `111`.', 'overloads_consistency.py:111:0 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +""" +output = """ +overloads_consistency.py:24:0 Incompatible overload [43]: The return type of overloaded function `return_type` (`str`) is incompatible with the return type of the implementation (`int`). +overloads_consistency.py:40:0 Incompatible overload [43]: The implementation of `parameter_type` does not accept all possible arguments of overload defined on line `40`. +overloads_consistency.py:107:0 Incompatible overload [43]: The return type of overloaded function `decorated` (`None`) is incompatible with the return type of the implementation (`bytes`). +overloads_consistency.py:107:0 Incompatible overload [43]: The implementation of `decorated` does not accept all possible arguments of overload defined on line `107`. +overloads_consistency.py:111:0 Incompatible overload [43]: The return type of overloaded function `decorated` (`str`) is incompatible with the return type of the implementation (`bytes`). +overloads_consistency.py:111:0 Incompatible overload [43]: The implementation of `decorated` does not accept all possible arguments of overload defined on line `111`. +overloads_consistency.py:111:0 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +""" diff --git a/conformance/results/pyre/overloads_definitions.toml b/conformance/results/pyre/overloads_definitions.toml new file mode 100644 index 00000000..fc29f762 --- /dev/null +++ b/conformance/results/pyre/overloads_definitions.toml @@ -0,0 +1,32 @@ +conformant = "Partial" +conformance_automated = "Fail" +notes = """ +Does not allow an overload with no implementation in a Protocol or an abstract base class. +Expects @final/@override on all overloads and implementation, instead of implementation only. +""" +errors_diff = """ +Line 245: Expected 1 errors +Lines 148, 150: Expected error (tag 'invalid_final_2') +Line 40: Unexpected errors ['overloads_definitions.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.'] +Line 51: Unexpected errors ['overloads_definitions.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] +Line 128: Unexpected errors ['overloads_definitions.py:128:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +Line 239: Unexpected errors ['overloads_definitions.py:239:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +""" +output = """ +overloads_definitions.py:15:0 Incompatible overload [43]: At least two overload signatures must be present. +overloads_definitions.py:27:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation. +overloads_definitions.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation. +overloads_definitions.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation. +overloads_definitions.py:64:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.not_abstract` must have an implementation. +overloads_definitions.py:80:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `80`. +overloads_definitions.py:85:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `85`. +overloads_definitions.py:88:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions.py:97:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `97`. +overloads_definitions.py:97:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions.py:128:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions.py:139:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions.py:204:4 Invalid override [40]: `overloads_definitions.Child.final_method` cannot override final method defined in `Base`. +overloads_definitions.py:220:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions.py:220:4 Invalid override [40]: `overloads_definitions.Child.bad_override` is decorated with @override, but no method of the same name exists in superclasses of `Child`. +overloads_definitions.py:239:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +""" diff --git a/conformance/results/pyre/overloads_definitions_stub.toml b/conformance/results/pyre/overloads_definitions_stub.toml new file mode 100644 index 00000000..93772e38 --- /dev/null +++ b/conformance/results/pyre/overloads_definitions_stub.toml @@ -0,0 +1,27 @@ +conformant = "Partial" +notes = """ +Expects @final and @override to be present on all overloads, not just first. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 168: Expected 1 errors +Lines 80, 82, 85, 87: Expected error (tag 'invalid_final') +Lines 96, 98, 101: Expected error (tag 'invalid_final_2') +Lines 122, 128, 129, 133: Expected error (tag 'override-final') +Line 75: Unexpected errors ['overloads_definitions_stub.pyi:75:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +Line 91: Unexpected errors ['overloads_definitions_stub.pyi:91:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +Line 146: Unexpected errors ['overloads_definitions_stub.pyi:146:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +Line 162: Unexpected errors ['overloads_definitions_stub.pyi:162:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +Line 174: Unexpected errors ['overloads_definitions_stub.pyi:174:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +""" +output = """ +overloads_definitions_stub.pyi:14:0 Incompatible overload [43]: At least two overload signatures must be present. +overloads_definitions_stub.pyi:43:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions_stub.pyi:52:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions_stub.pyi:75:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions_stub.pyi:91:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions_stub.pyi:142:4 Invalid override [40]: `overloads_definitions_stub.Child.bad_override` is decorated with @override, but no method of the same name exists in superclasses of `Child`. +overloads_definitions_stub.pyi:146:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions_stub.pyi:162:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions_stub.pyi:174:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +""" diff --git a/conformance/results/pyre/overloads_evaluation.toml b/conformance/results/pyre/overloads_evaluation.toml new file mode 100644 index 00000000..9a848686 --- /dev/null +++ b/conformance/results/pyre/overloads_evaluation.toml @@ -0,0 +1,43 @@ +conformant = "Partial" +notes = """ +Does not expand boolean arguments to Literal[True] and Literal[False]. +Does not expand enum arguments to literal variants. +Does not expand type[A | B] to type[A] and type[B]. +Does not expand tuple arguments to possible combinations. +Does not prefer variadic match to indeterminate-length unpacked argument. +Does not treat multiple matches due to gradual types as ambiguous. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 81: Unexpected errors ['overloads_evaluation.py:81:23 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`.'] +Line 82: Unexpected errors ['overloads_evaluation.py:82:4 Assert type [70]: Expected `Union[int, str]` but got `str`.'] +Line 106: Unexpected errors ['overloads_evaluation.py:106:23 Incompatible parameter type [6]: In call `expand_bool`, for 1st positional argument, expected `typing_extensions.Literal[False]` but got `bool`.'] +Line 107: Unexpected errors ['overloads_evaluation.py:107:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`.'] +Line 129: Unexpected errors ['overloads_evaluation.py:129:23 Incompatible parameter type [6]: In call `expand_enum`, for 1st positional argument, expected `typing_extensions.Literal[Color.RED]` but got `Color`.'] +Line 130: Unexpected errors ['overloads_evaluation.py:130:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`.'] +Line 147: Unexpected errors ['overloads_evaluation.py:147:29 Incompatible parameter type [6]: In call `expand_type_union`, for 1st positional argument, expected `Type[int]` but got `Type[Union[int, str]]`.'] +Line 148: Unexpected errors ['overloads_evaluation.py:148:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] +Line 169: Unexpected errors ['overloads_evaluation.py:169:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`.'] +Line 170: Unexpected errors ['overloads_evaluation.py:170:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] +Line 196: Unexpected errors ['overloads_evaluation.py:196:4 Assert type [70]: Expected `int` but got `str`.'] +Line 224: Unexpected errors ['overloads_evaluation.py:224:4 Assert type [70]: Expected `typing.Any` but got `int`.'] +""" +output = """ +overloads_evaluation.py:32:0 Missing argument [20]: Call `example1` expects argument `x`. +overloads_evaluation.py:40:12 Incompatible parameter type [6]: In call `example1`, for 2nd positional argument, expected `str` but got `int`. +overloads_evaluation.py:45:9 Incompatible parameter type [6]: In call `example1`, for 1st positional argument, expected `str` but got `int`. +overloads_evaluation.py:81:23 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`. +overloads_evaluation.py:82:4 Assert type [70]: Expected `Union[int, str]` but got `str`. +overloads_evaluation.py:89:13 Incompatible parameter type [6]: In call `example2`, for 1st positional argument, expected `int` but got `Union[int, str]`. +overloads_evaluation.py:89:16 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`. +overloads_evaluation.py:106:23 Incompatible parameter type [6]: In call `expand_bool`, for 1st positional argument, expected `typing_extensions.Literal[False]` but got `bool`. +overloads_evaluation.py:107:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`. +overloads_evaluation.py:129:23 Incompatible parameter type [6]: In call `expand_enum`, for 1st positional argument, expected `typing_extensions.Literal[Color.RED]` but got `Color`. +overloads_evaluation.py:130:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`. +overloads_evaluation.py:147:29 Incompatible parameter type [6]: In call `expand_type_union`, for 1st positional argument, expected `Type[int]` but got `Type[Union[int, str]]`. +overloads_evaluation.py:148:4 Assert type [70]: Expected `Union[int, str]` but got `int`. +overloads_evaluation.py:169:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`. +overloads_evaluation.py:170:4 Assert type [70]: Expected `Union[int, str]` but got `int`. +overloads_evaluation.py:196:4 Assert type [70]: Expected `int` but got `str`. +overloads_evaluation.py:224:4 Assert type [70]: Expected `typing.Any` but got `int`. +""" diff --git a/conformance/results/pyre/overloads_overlap.toml b/conformance/results/pyre/overloads_overlap.toml new file mode 100644 index 00000000..24996c18 --- /dev/null +++ b/conformance/results/pyre/overloads_overlap.toml @@ -0,0 +1,11 @@ +conformant = "Partial" +notes = """ +Does not check for partially overlapping overloads with inconsistent return type. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 14: Expected 1 errors +""" +output = """ +overloads_overlap.py:36:0 Incompatible overload [43]: The overloaded function `full_overlap` on line 36 will never be matched. The signature `(x: bool) -> bool` is the same or broader. +""" diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index 94de3055..2904f5c8 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 7.3 +test_duration = 6.0 diff --git a/conformance/results/pyright/overloads_basic.toml b/conformance/results/pyright/overloads_basic.toml index 5171f52e..14bfb58b 100644 --- a/conformance/results/pyright/overloads_basic.toml +++ b/conformance/results/pyright/overloads_basic.toml @@ -1,10 +1,10 @@ conformant = "Pass" +notes = """ +""" output = """ -overloads_basic.py:37:1 - error: No overloads for "__getitem__" match the provided arguments (reportCallIssue) -overloads_basic.py:37:1 - error: Argument of type "Literal['']" cannot be assigned to parameter "__s" of type "slice[Any, Any, Any]" in function "__getitem__" +overloads_basic.py:39:1 - error: No overloads for "__getitem__" match the provided arguments (reportCallIssue) +overloads_basic.py:39:1 - error: Argument of type "Literal['']" cannot be assigned to parameter "__s" of type "slice[Any, Any, Any]" in function "__getitem__"   "Literal['']" is not assignable to "slice[Any, Any, Any]" (reportArgumentType) -overloads_basic.py:63:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) -overloads_basic.py:75:5 - error: "func2" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) """ conformance_automated = "Pass" errors_diff = """ diff --git a/conformance/results/pyright/overloads_consistency.toml b/conformance/results/pyright/overloads_consistency.toml new file mode 100644 index 00000000..56f06507 --- /dev/null +++ b/conformance/results/pyright/overloads_consistency.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_consistency.py:27:5 - error: Overloaded implementation is not consistent with signature of overload 2 +  Function return type "str" is incompatible with type "int" +    "str" is not assignable to "int" (reportInconsistentOverload) +overloads_consistency.py:43:5 - error: Overloaded implementation is not consistent with signature of overload 2 +  Type "(x: int) -> (int | str)" is not assignable to type "(x: str) -> str" +    Parameter 1: type "str" is incompatible with type "int" +      "str" is not assignable to "int" (reportInconsistentOverload) +""" diff --git a/conformance/results/pyright/overloads_definitions.toml b/conformance/results/pyright/overloads_definitions.toml new file mode 100644 index 00000000..6a28a589 --- /dev/null +++ b/conformance/results/pyright/overloads_definitions.toml @@ -0,0 +1,32 @@ +conformant = "Partial" +notes = """ +Allows @final/@override on all overloads and implementation; should be implementation-only. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 245: Expected 1 errors +Lines 148, 150: Expected error (tag 'invalid_final_2') +""" +output = """ +overloads_definitions.py:15:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) +overloads_definitions.py:27:5 - error: "func2" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_definitions.py:64:9 - error: "not_abstract" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_definitions.py:80:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) +overloads_definitions.py:88:9 - error: Overloaded implementation is not consistent with signature of overload 1 +  Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: int) -> int" +    Parameter name mismatch: "x" versus "self" +    Parameter 1: type "int" is incompatible with type "Self@C" +      Type "int" is not assignable to type "Self@C" +    Extra parameter "x" (reportInconsistentOverload) +overloads_definitions.py:88:9 - error: Overloaded implementation is not consistent with signature of overload 2 +  Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: str) -> str" +    Parameter name mismatch: "x" versus "self" +    Parameter 1: type "str" is incompatible with type "Self@C" +      Type "str" is not assignable to type "Self@C" +    Extra parameter "x" (reportInconsistentOverload) +overloads_definitions.py:93:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) +overloads_definitions.py:97:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) +overloads_definitions.py:135:9 - error: Overload for "invalid_final" is marked @final but implementation is not (reportInconsistentOverload) +overloads_definitions.py:204:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) +overloads_definitions.py:220:9 - error: Method "bad_override" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) +""" diff --git a/conformance/results/pyright/overloads_definitions_stub.toml b/conformance/results/pyright/overloads_definitions_stub.toml new file mode 100644 index 00000000..6725a7a3 --- /dev/null +++ b/conformance/results/pyright/overloads_definitions_stub.toml @@ -0,0 +1,20 @@ +conformant = "Partial" +notes = """ +Does not enforce that @final/@override in a stub should be only on first overload. +Does not enforce @override when correctly used with an overloaded method in a stub file. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 168: Expected 1 errors +Lines 80, 82, 85, 87: Expected error (tag 'invalid_final') +Lines 96, 98, 101: Expected error (tag 'invalid_final_2') +Lines 140, 142: Expected error (tag 'bad_override') +""" +output = """ +overloads_definitions_stub.pyi:14:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) +overloads_definitions_stub.pyi:38:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) +overloads_definitions_stub.pyi:38:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) +overloads_definitions_stub.pyi:48:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) +overloads_definitions_stub.pyi:52:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) +overloads_definitions_stub.pyi:133:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) +""" diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml new file mode 100644 index 00000000..15c1a772 --- /dev/null +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -0,0 +1,46 @@ +conformant = "Partial" +notes = """ +Does not expand boolean arguments to Literal[True] and Literal[False]. +Does not expand enum arguments to literal variants. +Does not expand tuple arguments to possible combinations. +Does not prefer variadic match to indeterminate-length unpacked argument. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 106: Unexpected errors ['overloads_evaluation.py:106:12 - error: No overloads for "expand_bool" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:106:24 - error: Argument of type "bool" cannot be assigned to parameter "x" of type "Literal[True]" in function "expand_bool"'] +Line 107: Unexpected errors ['overloads_evaluation.py:107:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] +Line 129: Unexpected errors ['overloads_evaluation.py:129:12 - error: No overloads for "expand_enum" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:129:24 - error: Argument of type "Color" cannot be assigned to parameter "x" of type "Literal[Color.BLUE]" in function "expand_enum"'] +Line 130: Unexpected errors ['overloads_evaluation.py:130:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] +Line 169: Unexpected errors ['overloads_evaluation.py:169:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:169:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple"'] +Line 170: Unexpected errors ['overloads_evaluation.py:170:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure)'] +Line 196: Unexpected errors ['overloads_evaluation.py:196:17 - error: "assert_type" mismatch: expected "int" but received "str" (reportAssertTypeFailure)'] +""" +output = """ +overloads_evaluation.py:32:1 - error: No overloads for "example1" match the provided arguments +  Argument types: () (reportCallIssue) +overloads_evaluation.py:40:13 - error: Argument of type "Literal[1]" cannot be assigned to parameter "y" of type "str" in function "example1" +  "Literal[1]" is not assignable to "str" (reportArgumentType) +overloads_evaluation.py:45:10 - error: Argument of type "Literal[1]" cannot be assigned to parameter "x" of type "str" in function "example1" +  "Literal[1]" is not assignable to "str" (reportArgumentType) +overloads_evaluation.py:89:5 - error: No overloads for "example2" match the provided arguments (reportCallIssue) +overloads_evaluation.py:89:14 - error: Argument of type "int | str" cannot be assigned to parameter "x" of type "int" in function "example2" +  Type "int | str" is not assignable to type "int" +    "str" is not assignable to "int" (reportArgumentType) +overloads_evaluation.py:89:17 - error: Argument of type "int | str" cannot be assigned to parameter "y" of type "int" in function "example2" +  Type "int | str" is not assignable to type "int" +    "str" is not assignable to "int" (reportArgumentType) +overloads_evaluation.py:106:12 - error: No overloads for "expand_bool" match the provided arguments (reportCallIssue) +overloads_evaluation.py:106:24 - error: Argument of type "bool" cannot be assigned to parameter "x" of type "Literal[True]" in function "expand_bool" +  "bool" is not assignable to type "Literal[True]" (reportArgumentType) +overloads_evaluation.py:107:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure) +overloads_evaluation.py:129:12 - error: No overloads for "expand_enum" match the provided arguments (reportCallIssue) +overloads_evaluation.py:129:24 - error: Argument of type "Color" cannot be assigned to parameter "x" of type "Literal[Color.BLUE]" in function "expand_enum" +  "Color" is not assignable to type "Literal[Color.BLUE]" (reportArgumentType) +overloads_evaluation.py:130:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure) +overloads_evaluation.py:169:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue) +overloads_evaluation.py:169:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple" +  Type "int | str" is not assignable to type "str" +    "int" is not assignable to "str" (reportArgumentType) +overloads_evaluation.py:170:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure) +overloads_evaluation.py:196:17 - error: "assert_type" mismatch: expected "int" but received "str" (reportAssertTypeFailure) +""" diff --git a/conformance/results/pyright/overloads_overlap.toml b/conformance/results/pyright/overloads_overlap.toml new file mode 100644 index 00000000..8527f525 --- /dev/null +++ b/conformance/results/pyright/overloads_overlap.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_overlap.py:14:5 - error: Overload 1 for "is_one" overlaps overload 2 and returns an incompatible type (reportOverlappingOverload) +overloads_overlap.py:36:5 - error: Overload 2 for "full_overlap" will never be used because its parameters overlap overload 1 (reportOverlappingOverload) +""" diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index e8e963b7..1ae5a35b 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ version = "pyright 1.1.391" -test_duration = 1.2 +test_duration = 1.5 diff --git a/conformance/results/pytype/overloads_basic.toml b/conformance/results/pytype/overloads_basic.toml index 06e69b8c..d1e57904 100644 --- a/conformance/results/pytype/overloads_basic.toml +++ b/conformance/results/pytype/overloads_basic.toml @@ -1,20 +1,18 @@ conformant = "Partial" notes = """ -Does not reject a function with a single @overload signature. -Does not reject a function with @overload signature but no implementation. """ output = """ -overloads_basic.py:31:20: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in __getitem__: bad return type [bad-return-type] +overloads_basic.py:33:20: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in __getitem__: bad return type [bad-return-type] return b"" \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_basic.py:37:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : unsupported operand type(s) for item retrieval: Bytes and str [unsupported-operands] +overloads_basic.py:39:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : unsupported operand type(s) for item retrieval: Bytes and str [unsupported-operands] b[""] # E: no matching overload \u001b[1m\u001b[31m~~~~~\u001b[39m\u001b[0m -overloads_basic.py:58:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in map: bad return type [bad-return-type] +overloads_basic.py:60:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in map: bad return type [bad-return-type] pass \u001b[1m\u001b[31m~~~~\u001b[39m\u001b[0m @@ -22,8 +20,6 @@ overloads_basic.py:58:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in map: ba """ conformance_automated = "Fail" errors_diff = """ -Lines 62, 63: Expected error (tag 'func1') -Lines 74, 75: Expected error (tag 'func2') -Line 31: Unexpected errors ['overloads_basic.py:31:20: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in __getitem__: bad return type [bad-return-type]'] -Line 58: Unexpected errors ['overloads_basic.py:58:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in map: bad return type [bad-return-type]'] +Line 33: Unexpected errors ['overloads_basic.py:33:20: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in __getitem__: bad return type [bad-return-type]'] +Line 60: Unexpected errors ['overloads_basic.py:60:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in map: bad return type [bad-return-type]'] """ diff --git a/conformance/results/pytype/overloads_consistency.toml b/conformance/results/pytype/overloads_consistency.toml new file mode 100644 index 00000000..437e1cbb --- /dev/null +++ b/conformance/results/pytype/overloads_consistency.toml @@ -0,0 +1,56 @@ +conformant = "Fail" +notes = """ +Doesn't appear to validate overload consistency at all. +""" +conformance_automated = "Fail" +errors_diff = """ +Lines 24, 27: Expected error (tag 'return_type') +Lines 40, 43: Expected error (tag 'parameter_type') +Line 28: Unexpected errors ['overloads_consistency.py:28:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in return_type: bad return type [bad-return-type]'] +Line 44: Unexpected errors ['overloads_consistency.py:44:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in parameter_type: bad return type [bad-return-type]'] +Line 73: Unexpected errors ['overloads_consistency.py:73:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in returns_coroutine: bad return type [bad-return-type]'] +Line 88: Unexpected errors ['overloads_consistency.py:88:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in returns_coroutine_2: bad return type [bad-return-type]', 'overloads_consistency.py:88:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in returns_coroutine_2: bad return type [bad-return-type]'] +Line 107: Unexpected errors ['overloads_consistency.py:107:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Function decorated expects 0 arg(s), got 1 [wrong-arg-count]'] +Line 115: Unexpected errors ["overloads_consistency.py:115:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Missing parameter 'z' in call to function decorated [missing-parameter]"] +""" +output = """ +overloads_consistency.py:28:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in return_type: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_consistency.py:44:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in parameter_type: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_consistency.py:73:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in returns_coroutine: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_consistency.py:88:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in returns_coroutine_2: bad return type [bad-return-type] + + return _wrapped(x) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_consistency.py:88:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in returns_coroutine_2: bad return type [bad-return-type] + + return _wrapped(x) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_consistency.py:107:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function decorated expects 0 arg(s), got 1 [wrong-arg-count] + +def decorated() -> None: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~\u001b[39m\u001b[0m + +overloads_consistency.py:115:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Missing parameter 'z' in call to function decorated [missing-parameter] + +def decorated(y: bytes, z: bytes) -> bytes: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + return b"" +\u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +""" diff --git a/conformance/results/pytype/overloads_definitions.toml b/conformance/results/pytype/overloads_definitions.toml new file mode 100644 index 00000000..e5307735 --- /dev/null +++ b/conformance/results/pytype/overloads_definitions.toml @@ -0,0 +1,312 @@ +conformant = "Fail" +notes = """ +Does not reject a function with a single @overload signature. +Does not reject a function with @overload signature but no implementation. +Does not allow an overload with no implementation in a Protocol or an abstract base class. +Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`. +Does not enforce any rules on location of @final or @override decorators. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 245: Expected 1 errors +Lines 14, 15: Expected error (tag 'func1') +Lines 26, 27: Expected error (tag 'func2') +Lines 63, 64: Expected error (tag 'not_abstract') +Lines 78, 80, 85, 88: Expected error (tag 'func5') +Lines 91, 93, 97, 101: Expected error (tag 'func6') +Lines 133, 135, 139: Expected error (tag 'invalid_final') +Lines 148, 150: Expected error (tag 'invalid_final_2') +Line 6: Unexpected errors ['overloads_definitions.py:6:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : typing.override not supported yet [not-supported-yet]'] +Line 46: Unexpected errors ["overloads_definitions.py:46:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable]"] +Line 58: Unexpected errors ["overloads_definitions.py:58:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable]"] +Line 70: Unexpected errors ["overloads_definitions.py:70:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable]"] +Line 81: Unexpected errors ['overloads_definitions.py:81:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +Line 86: Unexpected errors ['overloads_definitions.py:86:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +Line 129: Unexpected errors ['overloads_definitions.py:129:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]', 'overloads_definitions.py:129:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] +Line 143: Unexpected errors ['overloads_definitions.py:143:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]', 'overloads_definitions.py:143:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]'] +Line 160: Unexpected errors ['overloads_definitions.py:160:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]', 'overloads_definitions.py:160:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]'] +Line 176: Unexpected errors ['overloads_definitions.py:176:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]', 'overloads_definitions.py:176:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] +Line 187: Unexpected errors ['overloads_definitions.py:187:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]', 'overloads_definitions.py:187:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]'] +Line 205: Unexpected errors ['overloads_definitions.py:205:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]', 'overloads_definitions.py:205:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] +Line 221: Unexpected errors ['overloads_definitions.py:221:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]', 'overloads_definitions.py:221:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]'] +Line 240: Unexpected errors ['overloads_definitions.py:240:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]', 'overloads_definitions.py:240:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] +Line 257: Unexpected errors ['overloads_definitions.py:257:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]', 'overloads_definitions.py:257:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]'] +""" +output = """ +overloads_definitions.py:6:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : typing.override not supported yet [not-supported-yet] + +from typing import ( +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + final, +\u001b[1m\u001b[31m~~~~~~~~~~\u001b[39m\u001b[0m + Protocol, +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + overload, +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + override, +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m +) +\u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_definitions.py:46:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:58:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:70:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:81:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:86:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:129:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:129:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:143:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:143:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:160:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final_2: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:160:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final_2: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:176:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:176:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:187:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:187:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:190:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Class Child overrides final method final_method, defined in base class Base [final-error] + +class Child(Base): # E[override-final] +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + # The correctly-decorated @final method `Base.final_method` should cause an +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # error if overridden in a child class (we use an overload here to avoid +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # questions of override LSP compatibility and focus only on the override): +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload # E[override-final] +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + def final_method(self, x: int) -> int: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def final_method(self, x: str) -> str: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + def final_method(self, x: int | str) -> int | str: # E[override-final] can't override final method +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + # This is the right way to mark an overload as @override (decorate +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # implementation only), so the use of @override should cause an error +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # (because there's no `Base.bad_override` method): +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload # E[bad_override] marked as override but doesn't exist in base +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + def bad_override(self, x: int) -> int: # E[bad_override] +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def bad_override(self, x: str) -> str: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @override +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def bad_override(self, x: int | str) -> int | str: # E[bad_override] +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + # This is also a correctly-decorated overloaded @override, which is +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # overriding a method that does exist in the base, so there should be no +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # error. We need both this test and the previous one, because in the +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # previous test, an incorrect error about the use of @override decorator +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # could appear on the same line as the expected error about overriding a +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # method that doesn't exist in base: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def good_override(self, x: int) -> int: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def good_override(self, x: str) -> str: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @override +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def good_override(self, x: int | str) -> int | str: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + # This is the wrong way to use @override with an overloaded method, and +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # should emit an error: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload # E: @override should appear only on implementation +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + @override +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def to_override(self, x: int) -> int: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + @override +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def to_override(self, x: str) -> str: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @override +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def to_override(self, x: int | str) -> int | str: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_definitions.py:205:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:205:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:212:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in Child: Attribute 'bad_override' not found on any parent class [override-error] + + def bad_override(self, x: int) -> int: # E[bad_override] + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_definitions.py:221:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:221:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:240:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:240:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:257:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:257:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +""" diff --git a/conformance/results/pytype/overloads_definitions_stub.toml b/conformance/results/pytype/overloads_definitions_stub.toml new file mode 100644 index 00000000..de2b6eda --- /dev/null +++ b/conformance/results/pytype/overloads_definitions_stub.toml @@ -0,0 +1,17 @@ +conformant = "Fail" +notes = """ +Does not enforce any of the specified rules regarding overload definitions. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 168: Expected 1 errors +Lines 13, 14: Expected error (tag 'func1') +Lines 37, 38, 43: Expected error (tag 'func5') +Lines 46, 48, 52: Expected error (tag 'func6') +Lines 80, 82, 85, 87: Expected error (tag 'invalid_final') +Lines 96, 98, 101: Expected error (tag 'invalid_final_2') +Lines 122, 128, 129, 133: Expected error (tag 'override-final') +Lines 140, 142: Expected error (tag 'bad_override') +""" +output = """ +""" diff --git a/conformance/results/pytype/overloads_evaluation.toml b/conformance/results/pytype/overloads_evaluation.toml new file mode 100644 index 00000000..004eea70 --- /dev/null +++ b/conformance/results/pytype/overloads_evaluation.toml @@ -0,0 +1,136 @@ +conformant = "Partial" +notes = """ +Does not perform argument expansion (on any types) when matching overloads. +Does not treat multiple matches due to gradual types as ambiguous. +Does not prefer variadic match to indeterminate-length unpacked argument. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 89: Expected 1 errors +Line 28: Unexpected errors ['overloads_evaluation.py:28:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example1: bad return type [bad-return-type]'] +Line 63: Unexpected errors ['overloads_evaluation.py:63:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example2: bad return type [bad-return-type]'] +Line 82: Unexpected errors ['overloads_evaluation.py:82:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_union: str [assert-type]'] +Line 103: Unexpected errors ['overloads_evaluation.py:103:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_bool: bad return type [bad-return-type]', 'overloads_evaluation.py:103:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_bool: bad return type [bad-return-type]'] +Line 106: Unexpected errors ['overloads_evaluation.py:106:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_bool: Function expand_bool was called with the wrong arguments [wrong-arg-types]'] +Line 107: Unexpected errors ['overloads_evaluation.py:107:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_bool: Any [assert-type]'] +Line 126: Unexpected errors ['overloads_evaluation.py:126:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_enum: bad return type [bad-return-type]'] +Line 129: Unexpected errors ['overloads_evaluation.py:129:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_enum: Function expand_enum was called with the wrong arguments [wrong-arg-types]'] +Line 130: Unexpected errors ['overloads_evaluation.py:130:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_enum: Any [assert-type]'] +Line 144: Unexpected errors ['overloads_evaluation.py:144:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_type_union: bad return type [bad-return-type]'] +Line 148: Unexpected errors ['overloads_evaluation.py:148:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_type_union: int [assert-type]'] +Line 166: Unexpected errors ['overloads_evaluation.py:166:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_tuple: bad return type [bad-return-type]'] +Line 170: Unexpected errors ['overloads_evaluation.py:170:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_tuple: int [assert-type]'] +Line 188: Unexpected errors ['overloads_evaluation.py:188:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in variadic: bad return type [bad-return-type]'] +Line 196: Unexpected errors ['overloads_evaluation.py:196:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_variadic: str [assert-type]'] +Line 217: Unexpected errors ['overloads_evaluation.py:217:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example4: bad return type [bad-return-type]'] +Line 224: Unexpected errors ['overloads_evaluation.py:224:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_example4: int [assert-type]'] +""" +output = """ +overloads_evaluation.py:28:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example1: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +Called from (traceback): + line 42, in current file +overloads_evaluation.py:32:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Missing parameter 'x' in call to function example1 [missing-parameter] + +example1() # E: no matching overload +\u001b[1m\u001b[31m~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:40:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function example1 was called with the wrong arguments [wrong-arg-types] + +example1(1, 1) # E: Literal[1] not assignable to str +\u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:45:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function example1 was called with the wrong arguments [wrong-arg-types] + +example1(1) # E: Literal[1] not assignable to str +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:63:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example2: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_evaluation.py:82:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_union: str [assert-type] + + assert_type(ret1, int | str) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:103:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_bool: bad return type [bad-return-type] + + return int(x) + \u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:103:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_bool: bad return type [bad-return-type] + + return int(x) + \u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:106:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_bool: Function expand_bool was called with the wrong arguments [wrong-arg-types] + + ret1 = expand_bool(v) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:107:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_bool: Any [assert-type] + + assert_type(ret1, Literal[0, 1]) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:126:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_enum: bad return type [bad-return-type] + + return x.value + \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:129:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_enum: Function expand_enum was called with the wrong arguments [wrong-arg-types] + + ret1 = expand_enum(v) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:130:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_enum: Any [assert-type] + + assert_type(ret1, Literal[0, 1]) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:144:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_type_union: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_evaluation.py:148:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_type_union: int [assert-type] + + assert_type(ret1, int | str) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:166:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_tuple: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_evaluation.py:170:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_tuple: int [assert-type] + + assert_type(ret1, int | str) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:188:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in variadic: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_evaluation.py:196:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_variadic: str [assert-type] + + assert_type(ret1, int) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:217:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example4: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_evaluation.py:224:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_example4: int [assert-type] + + assert_type(ret2, Any) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +""" diff --git a/conformance/results/pytype/overloads_overlap.toml b/conformance/results/pytype/overloads_overlap.toml new file mode 100644 index 00000000..598427b7 --- /dev/null +++ b/conformance/results/pytype/overloads_overlap.toml @@ -0,0 +1,23 @@ +conformant = "Fail" +notes = """ +Does not check for partially or fully overlapping overloads. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 14: Expected 1 errors +Line 36: Expected 1 errors +Line 22: Unexpected errors ['overloads_overlap.py:22:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in is_one: bad return type [bad-return-type]'] +Line 40: Unexpected errors ['overloads_overlap.py:40:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in full_overlap: bad return type [bad-return-type]'] +""" +output = """ +overloads_overlap.py:22:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in is_one: bad return type [bad-return-type] + + return x == 1 + \u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_overlap.py:40:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in full_overlap: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +""" diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 94cd5eee..ce6c9dce 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 37.2 +test_duration = 30.8 diff --git a/conformance/results/results.html b/conformance/results/results.html index e388ee38..c55cd345 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -158,17 +158,17 @@

Python Type System Conformance Test Results

- - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
mypy 1.14.0
-
1.6sec
+
mypy 1.14.1
+
1.7sec
pyright 1.1.391
-
1.2sec
+
1.5sec
pyre 0.9.23
-
7.3sec
+
6.0sec
pytype 2024.10.11
-
37.2sec
+
30.8sec
@@ -670,7 +670,37 @@

Python Type System Conformance Test Results

Pass Pass Pass
Partial

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Partial
     overloads_consistencyPassPass
Partial

Does not apply decorator transforms before checking overload consistency.

Fail

Doesn't appear to validate overload consistency at all.

     overloads_definitions
Partial

Does not allow an overload with no implementation in an abstract base class.

Allows @override to be on all overloads and implementation, instead of just implementation.

Partial

Allows @final/@override on all overloads and implementation; should be implementation-only.

Partial

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Expects @final/@override on all overloads and implementation, instead of implementation only.

Fail

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`.

Does not enforce any rules on location of @final or @override decorators.

     overloads_definitions_stub
Partial

Allows @override to appear in a stub file not on the first overload.

Partial

Does not enforce that @final/@override in a stub should be only on first overload.

Does not enforce @override when correctly used with an overloaded method in a stub file.

Partial

Expects @final and @override to be present on all overloads, not just first.

Fail

Does not enforce any of the specified rules regarding overload definitions.

     overloads_evaluation
Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Does not prefer variadic match to indeterminate-length unpacked argument.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand type[A | B] to type[A] and type[B].

Does not expand tuple arguments to possible combinations.

Does not prefer variadic match to indeterminate-length unpacked argument.

Does not treat multiple matches due to gradual types as ambiguous.

Partial

Does not perform argument expansion (on any types) when matching overloads.

Does not treat multiple matches due to gradual types as ambiguous.

Does not prefer variadic match to indeterminate-length unpacked argument.

     overloads_overlapPassPass
Partial

Does not check for partially overlapping overloads with inconsistent return type.

Fail

Does not check for partially or fully overlapping overloads.

Exceptions diff --git a/conformance/src/test_groups.py b/conformance/src/test_groups.py index f0145ff1..b974d401 100644 --- a/conformance/src/test_groups.py +++ b/conformance/src/test_groups.py @@ -4,6 +4,7 @@ """ from dataclasses import dataclass +from itertools import chain from pathlib import Path from typing import Mapping, Sequence @@ -39,7 +40,7 @@ def get_test_cases( # files that support one or more tests. test_cases = [ p - for p in Path(tests_dir).glob("*.py") + for p in chain(tests_dir.glob("*.py"), tests_dir.glob("*.pyi")) if p.name.split("_")[0] in test_group_names ] diff --git a/conformance/src/type_checker.py b/conformance/src/type_checker.py index b6b60fe7..21bec0d8 100644 --- a/conformance/src/type_checker.py +++ b/conformance/src/type_checker.py @@ -269,7 +269,7 @@ def parse_errors(self, output: Sequence[str]) -> dict[int, list[str]]: line_to_errors: dict[int, list[str]] = {} for line in output: # Ignore multi-line errors - if ".py:" not in line: + if ".py:" not in line and ".pyi:" not in line: continue # Ignore reveal_type errors if "Revealed type [-1]" in line: @@ -378,7 +378,7 @@ def parse_errors(self, output: Sequence[str]) -> dict[int, list[str]]: # annotations_forward_refs.py:103:1: unexpected indent [python-compiler-error] line_to_errors: dict[int, list[str]] = {} for line in output: - match = re.search(r"^[a-zA-Z0-9_]+.py:(\d+):(\d+): ", line) + match = re.search(r"^[a-zA-Z0-9_]+.pyi?:(\d+):(\d+): ", line) if match is not None: lineno = int(match.group(1)) line_to_errors.setdefault(int(lineno), []).append(line) diff --git a/conformance/tests/overloads_basic.py b/conformance/tests/overloads_basic.py index 86c81765..ec0fd069 100644 --- a/conformance/tests/overloads_basic.py +++ b/conformance/tests/overloads_basic.py @@ -1,16 +1,18 @@ """ -Tests the basic typing.overload behavior described in PEP 484. +Tests the behavior of typing.overload. """ # Specification: https://typing.readthedocs.io/en/latest/spec/overload.html#overload -# Note: The behavior of @overload is severely under-specified by PEP 484 leading -# to significant divergence in behavior across type checkers. This is something -# we will likely want to address in a future update to the typing spec. For now, -# this conformance test will cover only the most basic functionality described -# in PEP 484. - -from typing import Any, Callable, Iterable, Iterator, TypeVar, assert_type, overload +from typing import ( + Any, + Callable, + Iterable, + Iterator, + TypeVar, + assert_type, + overload, +) class Bytes: @@ -57,25 +59,3 @@ def map( def map(func: Any, iter1: Any, iter2: Any = ...) -> Any: pass - -# At least two overload signatures should be provided. -@overload # E[func1] -def func1() -> None: # E[func1]: At least two overloads must be present - ... - - -def func1() -> None: - pass - - -# > In regular modules, a series of @overload-decorated definitions must be -# > followed by exactly one non-@overload-decorated definition (for the same -# > function/method). -@overload # E[func2] -def func2(x: int) -> int: # E[func2]: no implementation - ... - - -@overload -def func2(x: str) -> str: - ... diff --git a/conformance/tests/overloads_consistency.py b/conformance/tests/overloads_consistency.py new file mode 100644 index 00000000..559fe114 --- /dev/null +++ b/conformance/tests/overloads_consistency.py @@ -0,0 +1,117 @@ +""" +Tests consistency of overloads with implementation. +""" + +from typing import Callable, Coroutine, overload + +# > If an overload implementation is defined, type checkers should validate +# > that it is consistent with all of its associated overload signatures. +# > The implementation should accept all potential sets of arguments +# > that are accepted by the overloads and should produce all potential return +# > types produced by the overloads. In typing terms, this means the input +# > signature of the implementation should be :term: to the input +# > signatures of all overloads, and the return type of all overloads should be +# > assignable to the return type of the implementation. + +# Return type of all overloads must be assignable to return type of +# implementation: + +@overload +def return_type(x: int) -> int: + ... + +@overload +def return_type(x: str) -> str: # E[return_type] + ... + +def return_type(x: int | str) -> int: # E[return_type] an overload returns `str`, not assignable to `int` + return 1 + + +# Input signature of implementation must be assignable to signature of each +# overload. We don't attempt a thorough testing of input signature +# assignability here; see `callables_subtyping.py` for that: + +@overload +def parameter_type(x: int) -> int: + ... + +@overload +def parameter_type(x: str) -> str: # E[parameter_type] + ... + +def parameter_type(x: int) -> int | str: # E[parameter_type] impl type of `x` must be assignable from overload types of `x` + return 1 + + +# > Overloads are allowed to use a mixture of ``async def`` and ``def`` statements +# > within the same overload definition. Type checkers should convert +# > ``async def`` statements to a non-async signature (wrapping the return +# > type in a ``Coroutine``) before testing for implementation consistency +# > and overlapping overloads (described below). + +# ...and also... + +# > When a type checker checks the implementation for consistency with overloads, +# > it should first apply any transforms that change the effective type of the +# > implementation including the presence of a ``yield`` statement in the +# > implementation body, the use of ``async def``, and the presence of additional +# > decorators. + +# An overload can explicitly return `Coroutine`, while the implementation is an +# `async def`: + +@overload +def returns_coroutine(x: int) -> Coroutine[None, None, int]: + ... + +@overload +async def returns_coroutine(x: str) -> str: + ... + +async def returns_coroutine(x: int | str) -> int | str: + return 1 + + +# The implementation can explicitly return `Coroutine`, while overloads are +# `async def`: + +@overload +async def returns_coroutine_2(x: int) -> int: + ... + +@overload +async def returns_coroutine_2(x: str) -> str: + ... + +def returns_coroutine_2(x: int | str) -> Coroutine[None, None, int | str]: + return _wrapped(x) + +async def _wrapped(x: int | str) -> int | str: + return 2 + +# Decorator transforms are applied before checking overload consistency: + +def _deco_1(f: Callable) -> Callable[[int], int]: + def wrapped(_x: int, /) -> int: + return 1 + return wrapped + +def _deco_2(f: Callable) -> Callable[[int | str], int | str]: + def wrapped(_x: int | str, /) -> int | str: + return 1 + return wrapped + +@overload +@_deco_1 +def decorated() -> None: + ... + +@overload +def decorated(x: str, /) -> str: + ... + +@_deco_2 +def decorated(y: bytes, z: bytes) -> bytes: + return b"" + diff --git a/conformance/tests/overloads_definitions.py b/conformance/tests/overloads_definitions.py new file mode 100644 index 00000000..a2323d7f --- /dev/null +++ b/conformance/tests/overloads_definitions.py @@ -0,0 +1,258 @@ +""" +Tests valid/invalid definition of overloaded functions. +""" + +from abc import ABC, abstractmethod +from typing import ( + final, + Protocol, + overload, + override, +) + +# > At least two @overload-decorated definitions must be present. +@overload # E[func1] +def func1() -> None: # E[func1]: At least two overloads must be present + ... + + +def func1() -> None: + pass + + +# > The ``@overload``-decorated definitions must be followed by an overload +# > implementation, which does not include an ``@overload`` decorator. Type +# > checkers should report an error or warning if an implementation is missing. +@overload # E[func2] +def func2(x: int) -> int: # E[func2]: no implementation + ... + + +@overload +def func2(x: str) -> str: + ... + + +# > Overload definitions within stub files, protocols, and on abstract methods +# > within abstract base classes are exempt from this check. +class MyProto(Protocol): + @overload + def func3(self, x: int) -> int: + ... + + + @overload + def func3(self, x: str) -> str: + ... + +class MyAbstractBase(ABC): + @overload + @abstractmethod + def func4(self, x: int) -> int: + ... + + + @overload + @abstractmethod + def func4(self, x: str) -> str: + ... + + # A non-abstract method in an abstract base class still requires an + # implementation: + + @overload # E[not_abstract] + def not_abstract(self, x: int) -> int: # E[not_abstract] no implementation + ... + + + @overload + def not_abstract(self, x: str) -> str: + ... + + +# > If one overload signature is decorated with ``@staticmethod`` or +# > ``@classmethod``, all overload signatures must be similarly decorated. The +# > implementation, if present, must also have a consistent decorator. Type +# > checkers should report an error if these conditions are not met. +class C: + @overload # E[func5] + @staticmethod + def func5(x: int) -> int: # E[func5] + ... + + @overload + @staticmethod + def func5(x: str) -> str: # E[func5] + ... + + def func5(self, x: int | str) -> int | str: # E[func5] + return 1 + + @overload # E[func6] + @classmethod + def func6(cls, x: int) -> int: # E[func6] + ... + + @overload + def func6(cls, x: str) -> str: # E[func6] + ... + + @classmethod + def func6(cls, x: int | str) -> int | str: # E[func6] + return 1 + + + +# > If a ``@final`` or ``@override`` decorator is supplied for a function with +# > overloads, the decorator should be applied only to the overload +# > implementation if it is present. If an overload implementation isn't present +# > (for example, in a stub file), the ``@final`` or ``@override`` decorator +# > should be applied only to the first overload. Type checkers should enforce +# > these rules and generate an error when they are violated. If a ``@final`` or +# > ``@override`` decorator follows these rules, a type checker should treat the +# > decorator as if it is present on all overloads. +class Base: + + # This is a good definition of an overloaded final method (@final decorator + # on implementation only): + + @overload + def final_method(self, x: int) -> int: + ... + + @overload + def final_method(self, x: str) -> str: + ... + + @final + def final_method(self, x: int | str) -> int | str: + ... + + # The @final decorator should not be on one of the overloads: + + @overload # E[invalid_final] @final should be on implementation only + @final + def invalid_final(self, x: int) -> int: # E[invalid_final] + ... + + @overload + def invalid_final(self, x: str) -> str: # E[invalid_final] + ... + + def invalid_final(self, x: int | str) -> int | str: + ... + + # The @final decorator should not be on multiple overloads and + # implementation: + + @overload # E[invalid_final_2] @final should be on implementation only + @final + def invalid_final_2(self, x: int) -> int: # E[invalid_final_2] + ... + + @overload + @final + def invalid_final_2(self, x: str) -> str: + ... + + @final + def invalid_final_2(self, x: int | str) -> int | str: + ... + + # These methods are just here for the @override test below. We use an + # overload because mypy doesn't like overriding a non-overloaded method + # with an overloaded one, even if LSP isn't violated. That could be its own + # specification question, but it's not what we're trying to test here: + + @overload + def good_override(self, x: int) -> int: + ... + + @overload + def good_override(self, x: str) -> str: + ... + + def good_override(self, x: int | str) -> int | str: + ... + + @overload + def to_override(self, x: int) -> int: + ... + + @overload + def to_override(self, x: str) -> str: + ... + + def to_override(self, x: int | str) -> int | str: + ... + + +class Child(Base): # E[override-final] + + # The correctly-decorated @final method `Base.final_method` should cause an + # error if overridden in a child class (we use an overload here to avoid + # questions of override LSP compatibility and focus only on the override): + + @overload # E[override-final] + def final_method(self, x: int) -> int: + ... + + @overload + def final_method(self, x: str) -> str: + ... + + def final_method(self, x: int | str) -> int | str: # E[override-final] can't override final method + ... + + # This is the right way to mark an overload as @override (decorate + # implementation only), so the use of @override should cause an error + # (because there's no `Base.bad_override` method): + + @overload # E[bad_override] marked as override but doesn't exist in base + def bad_override(self, x: int) -> int: # E[bad_override] + ... + + @overload + def bad_override(self, x: str) -> str: + ... + + @override + def bad_override(self, x: int | str) -> int | str: # E[bad_override] + ... + + # This is also a correctly-decorated overloaded @override, which is + # overriding a method that does exist in the base, so there should be no + # error. We need both this test and the previous one, because in the + # previous test, an incorrect error about the use of @override decorator + # could appear on the same line as the expected error about overriding a + # method that doesn't exist in base: + + @overload + def good_override(self, x: int) -> int: + ... + + @overload + def good_override(self, x: str) -> str: + ... + + @override + def good_override(self, x: int | str) -> int | str: + ... + + # This is the wrong way to use @override with an overloaded method, and + # should emit an error: + + @overload # E: @override should appear only on implementation + @override + def to_override(self, x: int) -> int: + ... + + @overload + @override + def to_override(self, x: str) -> str: + ... + + @override + def to_override(self, x: int | str) -> int | str: + ... + diff --git a/conformance/tests/overloads_definitions_stub.pyi b/conformance/tests/overloads_definitions_stub.pyi new file mode 100644 index 00000000..f86f2980 --- /dev/null +++ b/conformance/tests/overloads_definitions_stub.pyi @@ -0,0 +1,175 @@ +""" +Tests valid/invalid definition of overloaded functions in stub files, where the +rules differ from non-stubs, since an implementation is not required. +""" + +from typing import ( + final, + overload, + override, +) + +# > At least two @overload-decorated definitions must be present. +@overload # E[func1] +def func1() -> None: # E[func1]: At least two overloads must be present + ... + + +# > The ``@overload``-decorated definitions must be followed by an overload +# > implementation, which does not include an ``@overload`` decorator. Type +# > checkers should report an error or warning if an implementation is missing. +# > Overload definitions within stub files, protocols, and on abstract methods +# > within abstract base classes are exempt from this check. +@overload +def func2(x: int) -> int: + ... + + +@overload +def func2(x: str) -> str: + ... + +# > If one overload signature is decorated with ``@staticmethod`` or +# > ``@classmethod``, all overload signatures must be similarly decorated. The +# > implementation, if present, must also have a consistent decorator. Type +# > checkers should report an error if these conditions are not met. +class C: + @overload # E[func5] + def func5(x: int) -> int: # E[func5] + ... + + @overload + @staticmethod + def func5(x: str) -> str: # E[func5] + ... + + @overload # E[func6] + @classmethod + def func6(cls, x: int) -> int: # E[func6] + ... + + @overload + def func6(cls, x: str) -> str: # E[func6] + ... + + +# > If a ``@final`` or ``@override`` decorator is supplied for a function with +# > overloads, the decorator should be applied only to the overload +# > implementation if it is present. If an overload implementation isn't present +# > (for example, in a stub file), the ``@final`` or ``@override`` decorator +# > should be applied only to the first overload. Type checkers should enforce +# > these rules and generate an error when they are violated. If a ``@final`` or +# > ``@override`` decorator follows these rules, a type checker should treat the +# > decorator as if it is present on all overloads. +class Base: + + # This is a good definition of an overloaded final method in a stub (@final + # decorator on first overload only): + + @overload + @final + def final_method(self, x: int) -> int: + ... + + @overload + def final_method(self, x: str) -> str: + ... + + # The @final decorator should not be on multiple overloads: + + @overload # E[invalid_final] @final should be on first overload + @final + def invalid_final(self, x: int) -> int: # E[invalid_final] + ... + + @overload # E[invalid_final] + @final + def invalid_final(self, x: str) -> str: # E[invalid_final] + ... + + @overload + def invalid_final(self, x: bytes) -> bytes: + ... + + # The @final decorator should not be on all overloads: + + @overload # E[invalid_final_2] @final should be on first overload + @final + def invalid_final_2(self, x: int) -> int: # E[invalid_final_2] + ... + + @overload # E[invalid_final_2] + @final + def invalid_final_2(self, x: str) -> str: + ... + + # These methods are just here for the @override test below. We use an + # overload because mypy doesn't like overriding a non-overloaded method + # with an overloaded one, even if LSP isn't violated. That could be its own + # specification question, but it's not what we're trying to test here: + + @overload + def good_override(self, x: int) -> int: ... + @overload + def good_override(self, x: str) -> str: ... + + @overload + def to_override(self, x: int) -> int: ... + @overload + def to_override(self, x: str) -> str: ... + + +class Child(Base): # E[override-final] + + # The correctly-decorated @final method `Base.final_method` should cause an + # error if overridden in a child class (we use an overload here to avoid + # questions of override LSP compatibility and focus only on the override): + + @overload # E[override-final] + def final_method(self, x: int) -> int: # E[override-final] + ... + + @overload + def final_method(self, x: str) -> str: # E[override-final] can't override final method + ... + + # This is the right way to mark an overload as @override (decorate first + # overload only), so the use of @override should cause an error (because + # there's no `Base.bad_override` method): + + @overload # E[bad_override] marked as override but doesn't exist in base + @override + def bad_override(self, x: int) -> int: # E[bad_override] + ... + + @overload + def bad_override(self, x: str) -> str: + ... + + # This is also a correctly-decorated overloaded @override, which is + # overriding a method that does exist in the base, so there should be no + # error. We need both this test and the previous one, because in the + # previous test, an incorrect error about the use of @override decorator + # could appear on the same line as the expected error about overriding a + # method that doesn't exist in base: + + @overload + @override + def good_override(self, x: int) -> int: + ... + + @overload + def good_override(self, x: str) -> str: + ... + + # This is the wrong way to use @override with an overloaded method, and + # should emit an error: + + @overload # E: @override should appear only on first overload + def to_override(self, x: int) -> int: + ... + + @overload + @override + def to_override(self, x: str) -> str: + ... diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py new file mode 100644 index 00000000..1c14cbd2 --- /dev/null +++ b/conformance/tests/overloads_evaluation.py @@ -0,0 +1,224 @@ +""" +Tests for evaluation of calls to overloaded functions. +""" + +from enum import Enum +from typing import Any, assert_type, Literal, overload + + +# > Step 1: Examine the argument list to determine the number of +# > positional and keyword arguments. Use this information to eliminate any +# > overload candidates that are not plausible based on their +# > input signatures. + +# (There is no way to observe via conformance tests whether an implementation +# performs this step separately from the argument-type-testing step 2 below, so +# the separation of step 1 from step 2 is purely a presentation choice for the +# algorithm, not a conformance requirement.) + +@overload +def example1(x: int, y: str) -> int: + ... + +@overload +def example1(x: str) -> str: + ... + +def example1(x: int | str, y: str = "") -> int | str: + return 1 + +# > - If no candidate overloads remain, generate an error and stop. + +example1() # E: no matching overload + +# > - If only one candidate overload remains, it is the winning match. Evaluate +# > it as if it were a non-overloaded function call and stop. + +ret1 = example1(1, "") +assert_type(ret1, int) + +example1(1, 1) # E: Literal[1] not assignable to str + +ret3 = example1("") +assert_type(ret3, str) + +example1(1) # E: Literal[1] not assignable to str + + +# > Step 2: Evaluate each remaining overload as a regular (non-overloaded) +# > call to determine whether it is compatible with the supplied +# > argument list. Unlike step 1, this step considers the types of the parameters +# > and arguments. During this step, do not generate any user-visible errors. +# > Simply record which of the overloads result in evaluation errors. + +@overload +def example2(x: int, y: str, z: int) -> str: + ... + +@overload +def example2(x: int, y: int, z: int) -> int: + ... + +def example2(x: int, y: int | str, z: int) -> int | str: + return 1 + +# > - If only one overload evaluates without error, it is the winning match. +# > Evaluate it as if it were a non-overloaded function call and stop. + +ret5 = example2(1, 2, 3) +assert_type(ret5, int) + +# > Step 3: If step 2 produces errors for all overloads, perform +# > "argument type expansion". Union types can be expanded +# > into their constituent subtypes. For example, the type ``int | str`` can +# > be expanded into ``int`` and ``str``. + +# > - If all argument lists evaluate successfully, combine their +# > respective return types by union to determine the final return type +# > for the call, and stop. + +def check_expand_union(v: int | str) -> None: + ret1 = example2(1, v, 1) + assert_type(ret1, int | str) + +# > - If argument expansion has been applied to all arguments and one or +# > more of the expanded argument lists cannot be evaluated successfully, +# > generate an error and stop. + +def check_expand_union_2(v: int | str) -> None: + example2(v, v, 1) # E: no overload matches (str, ..., ...) + + +# > 2. ``bool`` should be expanded into ``Literal[True]`` and ``Literal[False]``. + +@overload +def expand_bool(x: Literal[False]) -> Literal[0]: + ... + +@overload +def expand_bool(x: Literal[True]) -> Literal[1]: + ... + +def expand_bool(x: bool) -> int: + return int(x) + +def check_expand_bool(v: bool) -> None: + ret1 = expand_bool(v) + assert_type(ret1, Literal[0, 1]) + + +# > 3. ``Enum`` types (other than those that derive from ``enum.Flag``) should +# > be expanded into their literal members. + +class Color(Enum): + RED = 1 + BLUE = 1 + +@overload +def expand_enum(x: Literal[Color.RED]) -> Literal[0]: + ... + +@overload +def expand_enum(x: Literal[Color.BLUE]) -> Literal[1]: + ... + +def expand_enum(x: Color) -> int: + return x.value + +def check_expand_enum(v: Color) -> None: + ret1 = expand_enum(v) + assert_type(ret1, Literal[0, 1]) + + +# > 4. ``type[A | B]`` should be expanded into ``type[A]`` and ``type[B]``. + +@overload +def expand_type_union(x: type[int]) -> int: + ... + +@overload +def expand_type_union(x: type[str]) -> str: + ... + +def expand_type_union(x: type[int] | type[str]) -> int | str: + return 1 + +def check_expand_type_union(v: type[int | str]) -> None: + ret1 = expand_type_union(v) + assert_type(ret1, int | str) + + +# > 5. Tuples of known length that contain expandable types should be expanded +# > into all possible combinations of their element types. For example, the type +# > ``tuple[int | str, bool]`` should be expanded into ``(int, Literal[True])``, +# > ``(int, Literal[False])``, ``(str, Literal[True])``, and +# > ``(str, Literal[False])``. + +@overload +def expand_tuple(x: tuple[int, int]) -> int: + ... + +@overload +def expand_tuple(x: tuple[int, str]) -> str: + ... + +def expand_tuple(x: tuple[int, int | str]) -> int | str: + return 1 + +def check_expand_tuple(v: int | str) -> None: + ret1 = expand_tuple((1, v)) + assert_type(ret1, int | str) + + +# > Step 4: If the argument list is compatible with two or more overloads, +# > determine whether one or more of the overloads has a variadic parameter +# > (either ``*args`` or ``**kwargs``) that maps to a corresponding argument +# > that supplies an indeterminate number of positional or keyword arguments. +# > If so, eliminate overloads that do not have a variadic parameter. + +@overload +def variadic(x: int, /) -> str: + ... + +@overload +def variadic(x: int, y: int, /, *args: int) -> int: + ... + +def variadic(*args: int) -> int | str: + return 1 + +# > - If this results in only one remaining candidate overload, it is +# > the winning match. Evaluate it as if it were a non-overloaded function +# > call and stop. + +def check_variadic(v: list[int]) -> None: + ret1 = variadic(*v) + assert_type(ret1, int) + + +# > Step 5: For each argument, determine whether all possible +# > :term:`materializations ` of the argument's type are assignable to +# > the corresponding parameter type for each of the remaining overloads. If so, +# > eliminate all of the subsequent remaining overloads. + +@overload +def example4(x: list[int], y: int) -> int: + ... + +@overload +def example4(x: list[str], y: str) -> int: + ... + +@overload +def example4(x: int, y: int) -> list[int]: + ... + +def example4(x: list[int] | list[str] | int, y: int | str) -> int | list[int]: + return 1 + +def check_example4(v1: list[Any], v2: Any): + ret1 = example4(v1, v2) + assert_type(ret1, int) + + ret2 = example4(v2, 1) + assert_type(ret2, Any) diff --git a/conformance/tests/overloads_overlap.py b/conformance/tests/overloads_overlap.py new file mode 100644 index 00000000..b65fa871 --- /dev/null +++ b/conformance/tests/overloads_overlap.py @@ -0,0 +1,40 @@ +""" +Tests checks for overlapping overloads. +""" + +from typing import Literal, overload + +# > If two overloads can accept the same set of arguments, they are said +# > to "partially overlap". If two overloads partially overlap, the return type +# > of the former overload should be assignable to the return type of the +# > latter overload. If this condition doesn't hold, it is indicative of a +# > programming error and should be reported by type checkers. + +@overload +def is_one(x: Literal[1]) -> Literal[True]: # E: overlapping overloads, inconsistent return type + ... + +@overload +def is_one(x: int) -> Literal[False]: + ... + +def is_one(x: int) -> bool: + return x == 1 + + +# > If all possible sets of arguments accepted by an overload are also always +# > accepted by an earlier overload, the two overloads are said to "fully overlap". +# > In this case, the latter overload will never be used. This condition +# > is indicative of a programming error and should be reported by type +# > checkers. + +@overload +def full_overlap(x: bool) -> bool: + ... + +@overload +def full_overlap(x: Literal[False]) -> int: # E: overload will never be used due to full overlap + ... + +def full_overlap(x: bool) -> int: + return 1 diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 3b534046..c23d875b 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -1,11 +1,21 @@ .. _`overload`: -``@overload`` +``Overloads`` ============= +In Python, it is common for callable objects to be polymorphic, meaning +they accept different types of arguments. It is also common for such +callables to return different types depending on the arguments passed to +them. Overloads provide a way to describe the accepted input signatures +and corresponding return types. + + +Overload definitions +^^^^^^^^^^^^^^^^^^^^ + The ``@overload`` decorator allows describing functions and methods -that support multiple different combinations of argument types. This -pattern is used frequently in builtin modules and types. For example, +that support multiple different combinations of argument types. This +pattern is used frequently in builtin modules and types. For example, the ``__getitem__()`` method of the ``bytes`` type can be described as follows:: @@ -18,9 +28,9 @@ follows:: @overload def __getitem__(self, s: slice) -> bytes: ... -This description is more precise than would be possible using unions -(which cannot express the relationship between the argument and return -types):: +This description is more precise than would be possible using unions, +which cannot express the relationship between the argument and return +types:: class bytes: ... @@ -54,15 +64,15 @@ Note that we could also easily add items to support ``map(None, ...)``:: iter2: Iterable[T2]) -> Iterable[tuple[T1, T2]]: ... Uses of the ``@overload`` decorator as shown above are suitable for -stub files. In regular modules, a series of ``@overload``-decorated +stub files. In regular modules, a series of ``@overload``-decorated definitions must be followed by exactly one non-``@overload``-decorated definition (for the same function/method). The ``@overload``-decorated definitions are for the benefit of the type checker only, since they will be overwritten by the non-``@overload``-decorated definition, while the latter is used at -runtime but should be ignored by a type checker. At runtime, calling -a ``@overload``-decorated function directly will raise -``NotImplementedError``. Here's an example of a non-stub overload +runtime but should be ignored by a type checker. At runtime, calling +an ``@overload``-decorated function directly will raise +``NotImplementedError``. Here's an example of a non-stub overload that can't easily be expressed using a union or a type variable:: @overload @@ -77,9 +87,9 @@ that can't easily be expressed using a union or a type variable:: def utf8(value): -A constrained ``TypeVar`` type can often be used instead of using the -``@overload`` decorator. For example, the definitions of ``concat1`` -and ``concat2`` in this stub file are equivalent:: +A constrained ``TypeVar`` type can sometimes be used instead of +using the ``@overload`` decorator. For example, the definitions +of ``concat1`` and ``concat2`` in this stub file are equivalent:: from typing import TypeVar @@ -99,8 +109,349 @@ variable is not sufficient. Another important difference between type variables such as ``AnyStr`` and using ``@overload`` is that the prior can also be used to define -constraints for generic class type parameters. For example, the type +constraints for generic class type parameters. For example, the type parameter of the generic class ``typing.IO`` is constrained (only ``IO[str]``, ``IO[bytes]`` and ``IO[Any]`` are valid):: class IO(Generic[AnyStr]): ... + + +Invalid overload definitions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Type checkers should enforce the following rules for overload definitions. + +At least two ``@overload``-decorated definitions must be present. If only +one is present, an error should be reported. + +The ``@overload``-decorated definitions must be followed by an overload +implementation, which does not include an ``@overload`` decorator. Type +checkers should report an error or warning if an implementation is missing. +Overload definitions within stub files, protocols, and on abstract methods +within abstract base classes are exempt from this check. + +If one overload signature is decorated with ``@staticmethod`` or +``@classmethod``, all overload signatures must be similarly decorated. The +implementation, if present, must also have a consistent decorator. Type +checkers should report an error if these conditions are not met. + +If a ``@final`` or ``@override`` decorator is supplied for a function with +overloads, the decorator should be applied only to the overload implementation +if it is present. If an overload implementation isn't present (for example, in +a stub file), the ``@final`` or ``@override`` decorator should be applied only +to the first overload. Type checkers should enforce these rules and generate +an error when they are violated. If a ``@final`` or ``@override`` decorator +follows these rules, a type checker should treat the decorator as if it is +present on all overloads. + +Overloads are allowed to use a mixture of ``async def`` and ``def`` statements +within the same overload definition. Type checkers should convert +``async def`` statements to a non-async signature (wrapping the return +type in a ``Coroutine``) before testing for implementation consistency +and overlapping overloads (described below). + + +Implementation consistency +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If an overload implementation is defined, type checkers should validate +that it is consistent with all of its associated overload signatures. +The implementation should accept all potential sets of arguments +that are accepted by the overloads and should produce all potential return +types produced by the overloads. In typing terms, this means the input +signature of the implementation should be :term: to the input +signatures of all overloads, and the return type of all overloads should be +assignable to the return type of the implementation. + +If the implementation is inconsistent with its overloads, a type checker +should report an error:: + + @overload + def func(x: str, /) -> str: ... + @overload + def func(x: int) -> int: ... + + # This implementation is inconsistent with the second overload + # because it does not accept a keyword argument ``x`` and the + # the overload's return type ``int`` is not assignable to the + implementation's return type ``str``. + def func(x: int | str, /) -> str: + return str(x) + +When a type checker checks the implementation for consistency with overloads, +it should first apply any transforms that change the effective type of the +implementation including the presence of a ``yield`` statement in the +implementation body, the use of ``async def``, and the presence of additional +decorators. + + +Overlapping overloads +^^^^^^^^^^^^^^^^^^^^^ + +If two overloads can accept the same set of arguments, they are said +to "partially overlap". If two overloads partially overlap, the return type +of the former overload should be assignable to the return type of the +latter overload. If this condition doesn't hold, it is indicative of a +programming error and should be reported by type checkers. The purpose of +this check is to prevent unsoundness of this form:: + + @overload + def is_one(x: Literal[1]) -> Literal[True]: ... + @overload + def is_one(x: int) -> Literal[False]: ... + + reveal_type(is_one(int(1))) # Reveals Literal[False], but True at runtime + +Type checkers may exempt certain magic methods from the above check +for conditions that are mandated by their usage in the runtime. For example, +the ``__get__`` method of a descriptor is often defined using overloads +that would partially overlap if the above rule is enforced. + +Type checkers may ignore the possibility of multiple inheritance or +intersections involving structural types for purposes of computing overlap. +In the following example, classes ``A`` and ``B`` could theoretically overlap +because there could be a common type ``C`` that derives from both ``A`` and +``B``, but type checkers may choose not to flag this as an overlapping +overload:: + + class A: ... + class B: ... + + @overload + def func(x: A) -> int: ... + @overload + def func(x: B) -> str: ... + +If all possible sets of arguments accepted by an overload are also always +accepted by an earlier overload, the two overloads are said to "fully overlap". +In this case, the latter overload will never be used. This condition +is indicative of a programming error and should be reported by type +checkers:: + + # These overloads fully overlap because the first overload + # accepts all arguments accepted by the second overload. + + @overload + def func[T](x: T) -> T: ... + @overload + def func(x: int) -> int: ... + + +Overload call evaluation +^^^^^^^^^^^^^^^^^^^^^^^^ + +When a type checker evaluates the call of an overloaded function, it +attempts to "match" the supplied arguments with one or more overloads. +This section describes the algorithm that type checkers should use +for overload matching. This algorithm should be applied even in the +presence of :ref:. + +Only the overloads (the ``@overload``-decorated signatures) should be +considered for matching purposes. The implementation, if provided, +should be ignored for purposes of overload matching. + + +Step 1: Examine the argument list to determine the number of +positional and keyword arguments. Use this information to eliminate any +overload candidates that are not plausible based on their +input signatures. + +- If no candidate overloads remain, generate an error and stop. +- If only one candidate overload remains, it is the winning match. Evaluate + it as if it were a non-overloaded function call and stop. +- If two or more candidate overloads remain, proceed to step 2. + + +Step 2: Evaluate each remaining overload as a regular (non-overloaded) +call to determine whether it is compatible with the supplied +argument list. Unlike step 1, this step considers the types of the parameters +and arguments. During this step, do not generate any user-visible errors. +Simply record which of the overloads result in evaluation errors. + +- If all overloads result in errors, proceed to step 3. +- If only one overload evaluates without error, it is the winning match. + Evaluate it as if it were a non-overloaded function call and stop. +- If two or more candidate overloads remain, proceed to step 4. + + +Step 3: If step 2 produces errors for all overloads, perform +"argument type expansion". Union types can be expanded +into their constituent subtypes. For example, the type ``int | str`` can +be expanded into ``int`` and ``str``. + +Type expansion should be performed one argument at a time from left to +right. Each expansion results in sets of effective argument types. +For example, if there are two arguments whose types evaluate to +``int | str`` and ``int | bytes``, expanding the first argument type +results in two sets of argument types: ``(int, int | bytes)`` and +``(str, int | bytes)``. If type expansion for the second argument is required, +four sets of argument types are produced: ``(int, int)``, ``(int, bytes)``, +``(str, int)``, and ``(str, bytes)``. + +After each argument's expansion, return to step 2 and evaluate all +expanded argument lists. + +- If all argument lists evaluate successfully, combine their + respective return types by union to determine the final return type + for the call, and stop. +- If argument expansion has been applied to all arguments and one or + more of the expanded argument lists cannot be evaluated successfully, + generate an error and stop. + + +For additional details about argument type expansion, see +`argument-type-expansion`_ below. + + +Step 4: If the argument list is compatible with two or more overloads, +determine whether one or more of the overloads has a variadic parameter +(either ``*args`` or ``**kwargs``) that maps to a corresponding argument +that supplies an indeterminate number of positional or keyword arguments. +If so, eliminate overloads that do not have a variadic parameter. + +- If this results in only one remaining candidate overload, it is + the winning match. Evaluate it as if it were a non-overloaded function + call and stop. +- If two or more candidate overloads remain, proceed to step 5. + + +Step 5: For each argument, determine whether all possible +:term:`materializations ` of the argument's type are assignable to +the corresponding parameter type for each of the remaining overloads. If so, +eliminate all of the subsequent remaining overloads. + +For example, if the argument type is ``list[Any]`` and there are three remaining +overloads with corresponding parameter types of ``list[int]``, ``list[Any]`` +and ``Any``. We can eliminate the third of the remaining overloads because +all materializations of ``list[Any]`` are assignable to ``list[Any]``, the +parameter in the second overload. We cannot eliminate the second overload +because there are possible materializations of ``list[Any]`` (for example, +``list[str]``) that are not assignable to ``list[int]``. + +Once this filtering process is applied for all arguments, examine the return +types of the remaining overloads. If these return types include type variables, +they should be replaced with their solved types. If the resulting return types +for all remaining overloads are :term:, proceed to step 6. + +If the return types are not equivalent, overload matching is ambiguous. In +this case, assume a return type of ``Any`` and stop. + + +Step 6: Choose the first remaining candidate overload as the winning +match. Evaluate it as if it were a non-overloaded function call and stop. + +Example 1:: + + @overload + def example1(x: int, y: str) -> int: ... + @overload + def example1(x: str) -> str: ... + + example1() # Error in step 1: no plausible overloads + example1(1, "") # Step 1 eliminates second overload + example1("") # Step 1 eliminates first overload + + example1("", "") # Error in step 2: no compatible overloads + example1(1) # Error in step 2: no compatible overloads + + +Example 2:: + + @overload + def example2(x: int, y: str, z: int) -> str: ... + @overload + def example2(x: int, y: int, z: int) -> int: ... + + def test(values: list[str | int]): + # In this example, argument type expansion is + # performed on the first two arguments. Expansion + # of the third is unnecessary. + r1 = example2(1, values[0], 1) + reveal_type(r1) # Should reveal str | int + + # Here, the types of all three arguments are expanded + # without success. + example2(values[0], values[1], values[2]) # Error in step 3 + + +Example 3:: + + @overload + def example3(x: int, /) -> tuple[int]: ... + @overload + def example3(x: int, y: int, /) -> tuple[int, int]: ... + @overload + def example3(*args: int) -> tuple[int, ...]: ... + + def test(val: list[int]): + # Step 1 eliminates second overload. Step 4 and + # step 5 do not apply. Step 6 picks the first + # overload. + r1 = example3(1) + reveal_type(r1) # Should reveal tuple[int] + + # Step 1 eliminates first overload. Step 4 and + # step 5 do not apply. Step 6 picks the second + # overload. + r2 = example3(1, 2) + reveal_type(r2) # Should reveal tuple[int, int] + + # Step 1 doesn't eliminate any overloads. Step 4 + # picks the third overload. + r3 = example3(*val) + reveal_type(r3) # Should reveal tuple[int, ...] + + +Example 4:: + + @overload + def example4(x: list[int], y: int) -> int: ... + @overload + def example4(x: list[str], y: str) -> int: ... + @overload + def example4(x: int, y: int) -> list[int]: ... + + def test(v1: list[Any], v2: Any): + # Step 2 eliminates the third overload. Step 5 + # determines that first and second overloads + # both apply and are ambiguous due to Any, but + # return types are consistent. + r1 = example4(v1, v2) + reveal_type(r1) # Reveals int + + # Step 2 eliminates the second overload. Step 5 + # determines that first and third overloads + # both apply and are ambiguous due to Any, and + # the return types are inconsistent. + r2 = example4(v2, 1) + reveal_type(r2) # Should reveal Any + + +.. _argument-type-expansion: + +Argument type expansion +^^^^^^^^^^^^^^^^^^^^^^^ + +When performing argument type expansion, a type that is equivalent to +a union of a finite set of subtypes should be expanded into its constituent +subtypes. This includes the following cases. + +1. Explicit unions: Each subtype of the union should be considered as a +separate argument type. For example, the type ``int | str`` should be expanded +into ``int`` and ``str``. + +2. ``bool`` should be expanded into ``Literal[True]`` and ``Literal[False]``. + +3. ``Enum`` types (other than those that derive from ``enum.Flag``) should +be expanded into their literal members. + +4. ``type[A | B]`` should be expanded into ``type[A]`` and ``type[B]``. + +5. Tuples of known length that contain expandable types should be expanded +into all possible combinations of their element types. For example, the type +``tuple[int | str, bool]`` should be expanded into ``(int, Literal[True])``, +``(int, Literal[False])``, ``(str, Literal[True])``, and +``(str, Literal[False])``. + +The above list may not be exhaustive, and additional cases may be added in +the future as the type system evolves.