From 59570beb57c2b07820ae2dc4f2084324eafc1f8a Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 11 Jul 2025 13:28:37 +0100 Subject: [PATCH] combine the two rules --- .github/mypy-primer-ty.toml | 2 +- crates/ty/docs/configuration.md | 2 +- crates/ty/docs/rules.md | 117 +++++++------- crates/ty/tests/cli/main.rs | 16 +- crates/ty/tests/cli/rule_selection.rs | 150 +++++++++--------- crates/ty/tests/file_watching.rs | 12 +- crates/ty_project/src/metadata/options.rs | 2 +- .../resources/mdtest/binary/booleans.md | 4 +- .../resources/mdtest/binary/integers.md | 19 +-- .../resources/mdtest/binary/unions.md | 2 +- .../mdtest/suppressions/ty_ignore.md | 38 ++--- .../mdtest/suppressions/type_ignore.md | 4 +- .../resources/mdtest/unreachable.md | 2 +- crates/ty_python_semantic/src/suppression.rs | 6 +- .../src/types/diagnostic.rs | 35 ++-- crates/ty_python_semantic/src/types/infer.rs | 26 ++- ty.schema.json | 28 ++-- 17 files changed, 220 insertions(+), 245 deletions(-) diff --git a/.github/mypy-primer-ty.toml b/.github/mypy-primer-ty.toml index 9317364fe2..eea5cb4774 100644 --- a/.github/mypy-primer-ty.toml +++ b/.github/mypy-primer-ty.toml @@ -5,4 +5,4 @@ [rules] possibly-unresolved-reference = "warn" unused-ignore-comment = "warn" -division-by-zero = "warn" +literal-math-error = "warn" diff --git a/crates/ty/docs/configuration.md b/crates/ty/docs/configuration.md index f2a39837fa..343d87f099 100644 --- a/crates/ty/docs/configuration.md +++ b/crates/ty/docs/configuration.md @@ -23,7 +23,7 @@ Valid severities are: ```toml [tool.ty.rules] possibly-unresolved-reference = "warn" -division-by-zero = "ignore" +literal-math-error = "ignore" ``` --- diff --git a/crates/ty/docs/rules.md b/crates/ty/docs/rules.md index 39e60651a9..b0f0817fe5 100644 --- a/crates/ty/docs/rules.md +++ b/crates/ty/docs/rules.md @@ -177,7 +177,7 @@ class B(A): ... Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L263) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L266) **What it does** @@ -202,7 +202,7 @@ class B(A, A): ... Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-kw-only) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L284) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L287) **What it does** @@ -306,7 +306,7 @@ def test(): -> "Literal[5]": Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L426) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L429) **What it does** @@ -334,7 +334,7 @@ class C(A, B): ... Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L450) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L453) **What it does** @@ -358,7 +358,7 @@ t[3] # IndexError: tuple index out of range Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20instance-layout-conflict) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L316) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L319) **What it does** @@ -445,7 +445,7 @@ an atypical memory layout. Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L470) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L473) **What it does** @@ -470,7 +470,7 @@ func("foo") # error: [invalid-argument-type] Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L510) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L513) **What it does** @@ -496,7 +496,7 @@ a: int = '' Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1514) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1517) **What it does** @@ -528,7 +528,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L532) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L535) **What it does** @@ -550,7 +550,7 @@ class A(42): ... # error: [invalid-base] Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L583) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L586) **What it does** @@ -575,7 +575,7 @@ with 1: Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L604) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L607) **What it does** @@ -602,7 +602,7 @@ a: str Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L627) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L630) **What it does** @@ -644,7 +644,7 @@ except ZeroDivisionError: Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L663) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L666) **What it does** @@ -675,7 +675,7 @@ class C[U](Generic[T]): ... Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L689) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L692) **What it does** @@ -708,7 +708,7 @@ def f(t: TypeVar("U")): ... Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L738) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L741) **What it does** @@ -740,7 +740,7 @@ class B(metaclass=f): ... Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L765) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L768) **What it does** @@ -788,7 +788,7 @@ def foo(x: int) -> int: ... Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L808) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L811) **What it does** @@ -812,7 +812,7 @@ def f(a: int = ''): ... Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L398) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L401) **What it does** @@ -844,7 +844,7 @@ TypeError: Protocols can only inherit from other protocols, got Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L828) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L831) Checks for `raise` statements that raise non-exceptions or use invalid @@ -891,7 +891,7 @@ def g(): Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L491) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L494) **What it does** @@ -914,7 +914,7 @@ def func() -> int: Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L871) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L874) **What it does** @@ -968,7 +968,7 @@ TODO #14889 Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L717) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L720) **What it does** @@ -993,7 +993,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L910) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L913) **What it does** @@ -1021,7 +1021,7 @@ TYPE_CHECKING = '' Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L934) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L937) **What it does** @@ -1049,7 +1049,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-call) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L986) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L989) **What it does** @@ -1081,7 +1081,7 @@ f(10) # Error Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-definition) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L958) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L961) **What it does** @@ -1113,7 +1113,7 @@ class C: Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1014) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1017) **What it does** @@ -1146,7 +1146,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1043) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1046) **What it does** @@ -1169,7 +1169,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x' Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1062) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1065) **What it does** @@ -1196,7 +1196,7 @@ func("string") # error: [no-matching-overload] Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20non-subscriptable) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1085) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1088) **What it does** @@ -1218,7 +1218,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1103) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1106) **What it does** @@ -1242,7 +1242,7 @@ for i in 34: # TypeError: 'int' object is not iterable Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1154) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1157) **What it does** @@ -1296,7 +1296,7 @@ def test(): -> "int": Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1490) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1493) **What it does** @@ -1324,7 +1324,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1245) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1248) **What it does** @@ -1351,7 +1351,7 @@ class B(A): ... # Error raised here Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1290) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1293) **What it does** @@ -1376,7 +1376,7 @@ f("foo") # Error raised here Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1268) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1271) **What it does** @@ -1402,7 +1402,7 @@ def _(x: int): Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1311) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1314) **What it does** @@ -1446,7 +1446,7 @@ class A: Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1368) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1371) **What it does** @@ -1471,7 +1471,7 @@ f(x=1, y=2) # Error raised here Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1389) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1392) **What it does** @@ -1497,7 +1497,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo' Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1411) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1414) **What it does** @@ -1520,7 +1520,7 @@ import foo # ModuleNotFoundError: No module named 'foo' Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1430) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1433) **What it does** @@ -1543,7 +1543,7 @@ print(x) # NameError: name 'x' is not defined Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1123) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1126) **What it does** @@ -1578,7 +1578,7 @@ b1 < b2 < b1 # exception raised here Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1449) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1452) **What it does** @@ -1604,7 +1604,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A' Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1471) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1474) **What it does** @@ -1655,7 +1655,7 @@ a = 20 / 0 # type: ignore Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-attribute) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1175) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1178) **What it does** @@ -1711,7 +1711,7 @@ A()[0] # TypeError: 'A' object is not subscriptable Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-import) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1197) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1200) **What it does** @@ -1741,7 +1741,7 @@ from module import a # ImportError: cannot import name 'a' from 'module' Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1542) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1545) **What it does** @@ -1766,7 +1766,7 @@ cast(int, f()) # Redundant Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1350) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1353) **What it does** @@ -1809,7 +1809,7 @@ a = 20 / 0 # ty: ignore[division-by-zer] Use instead: ```py -a = 20 / 0 # ty: ignore[division-by-zero] +a = 20 / 0 # ty: ignore[literal-math-error] ``` ## `unsupported-base` @@ -1817,7 +1817,7 @@ a = 20 / 0 # ty: ignore[division-by-zero] Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-base) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L550) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L553) **What it does** @@ -1849,26 +1849,29 @@ class D(C): ... # error: [unsupported-base] [method resolution order]: https://docs.python.org/3/glossary.html#term-method-resolution-order -## `division-by-zero` +## `literal-math-error` Default level: [`ignore`](../rules.md#rule-levels "This lint has a default level of 'ignore'.") · -[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero) · +[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20literal-math-error) · [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L245) **What it does** -It detects division by zero. +Detects runtime errors that would result from invalid math operations +between two objects with literal `int` types. Examples include division +by zero and negative bitshifts. **Why is this bad?** -Dividing by zero raises a `ZeroDivisionError` at runtime. +These math operations will lead to exceptions being raised at runtime. **Examples** ```python -5 / 0 +5 / 0 # `ZeroDivisionError` +1 << -1 # `ValueError: negative shift count` ``` ## `possibly-unresolved-reference` @@ -1876,7 +1879,7 @@ Dividing by zero raises a `ZeroDivisionError` at runtime. Default level: [`ignore`](../rules.md#rule-levels "This lint has a default level of 'ignore'.") · [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference) · -[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1223) +[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1226) **What it does** @@ -1917,7 +1920,7 @@ included by mistake, and should be removed to avoid confusion. **Examples** ```py -a = 20 / 2 # ty: ignore[division-by-zero] +a = 20 / 2 # ty: ignore[literal-math-error] ``` Use instead: diff --git a/crates/ty/tests/cli/main.rs b/crates/ty/tests/cli/main.rs index 14c237a729..8988272c44 100644 --- a/crates/ty/tests/cli/main.rs +++ b/crates/ty/tests/cli/main.rs @@ -327,7 +327,7 @@ fn user_configuration() -> anyhow::Result<()> { "project/ty.toml", r#" [rules] - division-by-zero = "warn" + literal-math-error = "warn" "#, ), ( @@ -356,7 +356,7 @@ fn user_configuration() -> anyhow::Result<()> { success: false exit_code: 1 ----- stdout ----- - warning[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + warning[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> main.py:2:5 | 2 | y = 4 / 0 @@ -364,7 +364,7 @@ fn user_configuration() -> anyhow::Result<()> { 3 | 4 | for a in range(0, int(y)): | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file error[unresolved-reference]: Name `prin` used when not defined --> main.py:7:1 @@ -384,13 +384,13 @@ fn user_configuration() -> anyhow::Result<()> { ); // The user-level configuration sets the severity for `unresolved-reference` to warn. - // Changing the level for `division-by-zero` has no effect, because the project-level configuration + // Changing the level for `literal-math-error` has no effect, because the project-level configuration // has higher precedence. case.write_file( config_directory.join("ty/ty.toml"), r#" [rules] - division-by-zero = "error" + literal-math-error = "error" unresolved-reference = "warn" "#, )?; @@ -401,7 +401,7 @@ fn user_configuration() -> anyhow::Result<()> { success: true exit_code: 0 ----- stdout ----- - warning[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + warning[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> main.py:2:5 | 2 | y = 4 / 0 @@ -409,7 +409,7 @@ fn user_configuration() -> anyhow::Result<()> { 3 | 4 | for a in range(0, int(y)): | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file warning[unresolved-reference]: Name `prin` used when not defined --> main.py:7:1 @@ -437,7 +437,7 @@ fn check_specific_paths() -> anyhow::Result<()> { ( "project/main.py", r#" - y = 4 / 0 # error: division-by-zero + y = 4 / 0 # error: literal-math-error "#, ), ( diff --git a/crates/ty/tests/cli/rule_selection.rs b/crates/ty/tests/cli/rule_selection.rs index 08560ac6c2..77b8aae54b 100644 --- a/crates/ty/tests/cli/rule_selection.rs +++ b/crates/ty/tests/cli/rule_selection.rs @@ -42,7 +42,7 @@ fn configuration_rule_severity() -> anyhow::Result<()> { "pyproject.toml", r#" [tool.ty.rules] - division-by-zero = "warn" # promote to warn + literal-math-error = "warn" # promote to warn unresolved-reference = "ignore" "#, )?; @@ -51,7 +51,7 @@ fn configuration_rule_severity() -> anyhow::Result<()> { success: true exit_code: 0 ----- stdout ----- - warning[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + warning[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> test.py:2:5 | 2 | y = 4 / 0 @@ -59,7 +59,7 @@ fn configuration_rule_severity() -> anyhow::Result<()> { 3 | 4 | for a in range(0, int(y)): | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file Found 1 diagnostic @@ -126,7 +126,7 @@ fn cli_rule_severity() -> anyhow::Result<()> { .arg("--ignore") .arg("unresolved-reference") .arg("--warn") - .arg("division-by-zero") + .arg("literal-math-error") .arg("--warn") .arg("unresolved-import"), @r" @@ -144,7 +144,7 @@ fn cli_rule_severity() -> anyhow::Result<()> { info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment info: rule `unresolved-import` was selected on the command line - warning[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + warning[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> test.py:4:5 | 2 | import does_not_exit @@ -154,7 +154,7 @@ fn cli_rule_severity() -> anyhow::Result<()> { 5 | 6 | for a in range(0, int(y)): | - info: rule `division-by-zero` was selected on the command line + info: rule `literal-math-error` was selected on the command line Found 2 diagnostics @@ -209,14 +209,14 @@ fn cli_rule_severity_precedence() -> anyhow::Result<()> { .arg("--warn") .arg("unresolved-reference") .arg("--warn") - .arg("division-by-zero") + .arg("literal-math-error") .arg("--ignore") .arg("unresolved-reference"), @r" success: true exit_code: 0 ----- stdout ----- - warning[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + warning[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> test.py:2:5 | 2 | y = 4 / 0 @@ -224,7 +224,7 @@ fn cli_rule_severity_precedence() -> anyhow::Result<()> { 3 | 4 | for a in range(0, int(y)): | - info: rule `division-by-zero` was selected on the command line + info: rule `literal-math-error` was selected on the command line Found 1 diagnostic @@ -299,21 +299,21 @@ fn overrides_basic() -> anyhow::Result<()> { "pyproject.toml", r#" [tool.ty.rules] - division-by-zero = "error" + literal-math-error = "error" unresolved-reference = "error" [[tool.ty.overrides]] include = ["tests/**"] [tool.ty.overrides.rules] - division-by-zero = "warn" + literal-math-error = "warn" unresolved-reference = "ignore" "#, ), ( "main.py", r#" - y = 4 / 0 # division-by-zero: error (global) + y = 4 / 0 # literal-math-error: error (global) x = 1 prin(x) # unresolved-reference: error (global) "#, @@ -321,7 +321,7 @@ fn overrides_basic() -> anyhow::Result<()> { ( "tests/test_main.py", r#" - y = 4 / 0 # division-by-zero: warn (override) + y = 4 / 0 # literal-math-error: warn (override) x = 1 prin(x) # unresolved-reference: ignore (override) "#, @@ -332,35 +332,35 @@ fn overrides_basic() -> anyhow::Result<()> { success: false exit_code: 1 ----- stdout ----- - error[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + error[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> main.py:2:5 | - 2 | y = 4 / 0 # division-by-zero: error (global) + 2 | y = 4 / 0 # literal-math-error: error (global) | ^^^^^ 3 | x = 1 4 | prin(x) # unresolved-reference: error (global) | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file error[unresolved-reference]: Name `prin` used when not defined --> main.py:4:1 | - 2 | y = 4 / 0 # division-by-zero: error (global) + 2 | y = 4 / 0 # literal-math-error: error (global) 3 | x = 1 4 | prin(x) # unresolved-reference: error (global) | ^^^^ | info: rule `unresolved-reference` was selected in the configuration file - warning[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + warning[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> tests/test_main.py:2:5 | - 2 | y = 4 / 0 # division-by-zero: warn (override) + 2 | y = 4 / 0 # literal-math-error: warn (override) | ^^^^^ 3 | x = 1 4 | prin(x) # unresolved-reference: ignore (override) | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file Found 3 diagnostics @@ -379,31 +379,31 @@ fn overrides_precedence() -> anyhow::Result<()> { "pyproject.toml", r#" [tool.ty.rules] - division-by-zero = "error" + literal-math-error = "error" # First override: all test files [[tool.ty.overrides]] include = ["tests/**"] [tool.ty.overrides.rules] - division-by-zero = "warn" + literal-math-error = "warn" # Second override: specific test file (takes precedence) [[tool.ty.overrides]] include = ["tests/important.py"] [tool.ty.overrides.rules] - division-by-zero = "ignore" + literal-math-error = "ignore" "#, ), ( "tests/test_main.py", r#" - y = 4 / 0 # division-by-zero: warn (first override) + y = 4 / 0 # literal-math-error: warn (first override) "#, ), ( "tests/important.py", r#" - y = 4 / 0 # division-by-zero: ignore (second override) + y = 4 / 0 # literal-math-error: ignore (second override) "#, ), ])?; @@ -412,13 +412,13 @@ fn overrides_precedence() -> anyhow::Result<()> { success: true exit_code: 0 ----- stdout ----- - warning[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + warning[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> tests/test_main.py:2:5 | - 2 | y = 4 / 0 # division-by-zero: warn (first override) + 2 | y = 4 / 0 # literal-math-error: warn (first override) | ^^^^^ | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file Found 1 diagnostic @@ -437,25 +437,25 @@ fn overrides_exclude() -> anyhow::Result<()> { "pyproject.toml", r#" [tool.ty.rules] - division-by-zero = "error" + literal-math-error = "error" [[tool.ty.overrides]] include = ["tests/**"] exclude = ["tests/important.py"] [tool.ty.overrides.rules] - division-by-zero = "warn" + literal-math-error = "warn" "#, ), ( "tests/test_main.py", r#" - y = 4 / 0 # division-by-zero: warn (override applies) + y = 4 / 0 # literal-math-error: warn (override applies) "#, ), ( "tests/important.py", r#" - y = 4 / 0 # division-by-zero: error (override excluded) + y = 4 / 0 # literal-math-error: error (override excluded) "#, ), ])?; @@ -464,21 +464,21 @@ fn overrides_exclude() -> anyhow::Result<()> { success: false exit_code: 1 ----- stdout ----- - error[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + error[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> tests/important.py:2:5 | - 2 | y = 4 / 0 # division-by-zero: error (override excluded) + 2 | y = 4 / 0 # literal-math-error: error (override excluded) | ^^^^^ | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file - warning[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + warning[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> tests/test_main.py:2:5 | - 2 | y = 4 / 0 # division-by-zero: warn (override applies) + 2 | y = 4 / 0 # literal-math-error: warn (override applies) | ^^^^^ | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file Found 2 diagnostics @@ -497,28 +497,28 @@ fn overrides_inherit_global() -> anyhow::Result<()> { "pyproject.toml", r#" [tool.ty.rules] - division-by-zero = "warn" + literal-math-error = "warn" unresolved-reference = "error" [[tool.ty.overrides]] include = ["tests/**"] [tool.ty.overrides.rules] - # Override only division-by-zero, unresolved-reference should inherit from global - division-by-zero = "ignore" + # Override only literal-math-error, unresolved-reference should inherit from global + literal-math-error = "ignore" "#, ), ( "main.py", r#" - y = 4 / 0 # division-by-zero: warn (global) + y = 4 / 0 # literal-math-error: warn (global) prin(y) # unresolved-reference: error (global) "#, ), ( "tests/test_main.py", r#" - y = 4 / 0 # division-by-zero: ignore (overridden) + y = 4 / 0 # literal-math-error: ignore (overridden) prin(y) # unresolved-reference: error (inherited from global) "#, ), @@ -528,19 +528,19 @@ fn overrides_inherit_global() -> anyhow::Result<()> { success: false exit_code: 1 ----- stdout ----- - warning[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + warning[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> main.py:2:5 | - 2 | y = 4 / 0 # division-by-zero: warn (global) + 2 | y = 4 / 0 # literal-math-error: warn (global) | ^^^^^ 3 | prin(y) # unresolved-reference: error (global) | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file error[unresolved-reference]: Name `prin` used when not defined --> main.py:3:1 | - 2 | y = 4 / 0 # division-by-zero: warn (global) + 2 | y = 4 / 0 # literal-math-error: warn (global) 3 | prin(y) # unresolved-reference: error (global) | ^^^^ | @@ -549,7 +549,7 @@ fn overrides_inherit_global() -> anyhow::Result<()> { error[unresolved-reference]: Name `prin` used when not defined --> tests/test_main.py:3:1 | - 2 | y = 4 / 0 # division-by-zero: ignore (overridden) + 2 | y = 4 / 0 # literal-math-error: ignore (overridden) 3 | prin(y) # unresolved-reference: error (inherited from global) | ^^^^ | @@ -572,12 +572,12 @@ fn overrides_invalid_include_glob() -> anyhow::Result<()> { "pyproject.toml", r#" [tool.ty.rules] - division-by-zero = "error" + literal-math-error = "error" [[tool.ty.overrides]] include = ["tests/[invalid"] # Invalid glob: unclosed bracket [tool.ty.overrides.rules] - division-by-zero = "warn" + literal-math-error = "warn" "#, ), ( @@ -603,7 +603,7 @@ fn overrides_invalid_include_glob() -> anyhow::Result<()> { 6 | include = ["tests/[invalid"] # Invalid glob: unclosed bracket | ^^^^^^^^^^^^^^^^ unclosed character class; missing ']' 7 | [tool.ty.overrides.rules] - 8 | division-by-zero = "warn" + 8 | literal-math-error = "warn" | "#); @@ -618,13 +618,13 @@ fn overrides_invalid_exclude_glob() -> anyhow::Result<()> { "pyproject.toml", r#" [tool.ty.rules] - division-by-zero = "error" + literal-math-error = "error" [[tool.ty.overrides]] include = ["tests/**"] exclude = ["***/invalid"] # Invalid glob: triple asterisk [tool.ty.overrides.rules] - division-by-zero = "warn" + literal-math-error = "warn" "#, ), ( @@ -651,7 +651,7 @@ fn overrides_invalid_exclude_glob() -> anyhow::Result<()> { 7 | exclude = ["***/invalid"] # Invalid glob: triple asterisk | ^^^^^^^^^^^^^ Too many stars at position 1 8 | [tool.ty.overrides.rules] - 9 | division-by-zero = "warn" + 9 | literal-math-error = "warn" | "#); @@ -666,12 +666,12 @@ fn overrides_missing_include_exclude() -> anyhow::Result<()> { "pyproject.toml", r#" [tool.ty.rules] - division-by-zero = "error" + literal-math-error = "error" [[tool.ty.overrides]] # Missing both include and exclude - should warn [tool.ty.overrides.rules] - division-by-zero = "warn" + literal-math-error = "warn" "#, ), ( @@ -689,7 +689,7 @@ fn overrides_missing_include_exclude() -> anyhow::Result<()> { warning[unnecessary-overrides-section]: Unnecessary `overrides` section --> pyproject.toml:5:1 | - 3 | division-by-zero = "error" + 3 | literal-math-error = "error" 4 | 5 | [[tool.ty.overrides]] | ^^^^^^^^^^^^^^^^^^^^^ This overrides section applies to all files @@ -700,13 +700,13 @@ fn overrides_missing_include_exclude() -> anyhow::Result<()> { info: Restrict the files by adding a pattern to `include` or `exclude`... info: or remove the `[[overrides]]` section and merge the configuration into the root `[rules]` table if the configuration should apply to all files - warning[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + warning[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> test.py:2:5 | 2 | y = 4 / 0 | ^^^^^ | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file Found 2 diagnostics @@ -725,12 +725,12 @@ fn overrides_empty_include() -> anyhow::Result<()> { "pyproject.toml", r#" [tool.ty.rules] - division-by-zero = "error" + literal-math-error = "error" [[tool.ty.overrides]] include = [] # Empty include - won't match any files [tool.ty.overrides.rules] - division-by-zero = "warn" + literal-math-error = "warn" "#, ), ( @@ -752,17 +752,17 @@ fn overrides_empty_include() -> anyhow::Result<()> { 6 | include = [] # Empty include - won't match any files | ^^ This `include` list is empty 7 | [tool.ty.overrides.rules] - 8 | division-by-zero = "warn" + 8 | literal-math-error = "warn" | info: Remove the `include` option to match all files or add a pattern to match specific files - error[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + error[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> test.py:2:5 | 2 | y = 4 / 0 | ^^^^^ | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file Found 2 diagnostics @@ -781,7 +781,7 @@ fn overrides_no_actual_overrides() -> anyhow::Result<()> { "pyproject.toml", r#" [tool.ty.rules] - division-by-zero = "error" + literal-math-error = "error" [[tool.ty.overrides]] include = ["*.py"] # Has patterns but no rule overrides @@ -803,7 +803,7 @@ fn overrides_no_actual_overrides() -> anyhow::Result<()> { warning[useless-overrides-section]: Useless `overrides` section --> pyproject.toml:5:1 | - 3 | division-by-zero = "error" + 3 | literal-math-error = "error" 4 | 5 | [[tool.ty.overrides]] | ^^^^^^^^^^^^^^^^^^^^^ This overrides section configures no rules @@ -814,13 +814,13 @@ fn overrides_no_actual_overrides() -> anyhow::Result<()> { info: Add a `[overrides.rules]` table... info: or remove the `[[overrides]]` section if there's nothing to override - error[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + error[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> test.py:2:5 | 2 | y = 4 / 0 | ^^^^^ | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file Found 2 diagnostics @@ -839,13 +839,13 @@ fn overrides_unknown_rules() -> anyhow::Result<()> { "pyproject.toml", r#" [tool.ty.rules] - division-by-zero = "error" + literal-math-error = "error" [[tool.ty.overrides]] include = ["tests/**"] [tool.ty.overrides.rules] - division-by-zero = "warn" + literal-math-error = "warn" division-by-zer = "error" # incorrect rule name "#, ), @@ -871,26 +871,26 @@ fn overrides_unknown_rules() -> anyhow::Result<()> { --> pyproject.toml:10:1 | 8 | [tool.ty.overrides.rules] - 9 | division-by-zero = "warn" + 9 | literal-math-error = "warn" 10 | division-by-zer = "error" # incorrect rule name | ^^^^^^^^^^^^^^^ | - error[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + error[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> main.py:2:5 | 2 | y = 4 / 0 | ^^^^^ | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file - warning[division-by-zero]: Cannot divide object of type `Literal[4]` by zero + warning[literal-math-error]: Cannot divide object of type `Literal[4]` by zero --> tests/test_main.py:2:5 | 2 | y = 4 / 0 | ^^^^^ | - info: rule `division-by-zero` was selected in the configuration file + info: rule `literal-math-error` was selected in the configuration file Found 3 diagnostics diff --git a/crates/ty/tests/file_watching.rs b/crates/ty/tests/file_watching.rs index 545c824b1a..427727710d 100644 --- a/crates/ty/tests/file_watching.rs +++ b/crates/ty/tests/file_watching.rs @@ -1687,7 +1687,7 @@ fn changes_to_user_configuration() -> anyhow::Result<()> { config_directory.join("ty/ty.toml").as_std_path(), r#" [rules] - division-by-zero = "ignore" + literal-math-error = "ignore" "#, )?; @@ -1710,12 +1710,12 @@ fn changes_to_user_configuration() -> anyhow::Result<()> { "Expected no diagnostics but got: {diagnostics:#?}" ); - // Enable division-by-zero in the user configuration with warning severity + // Enable literal-math-error in the user configuration with warning severity update_file( case.root_path().join("home/.config/ty/ty.toml"), r#" [rules] - division-by-zero = "warn" + literal-math-error = "warn" "#, )?; @@ -1753,7 +1753,7 @@ fn changes_to_config_file_override() -> anyhow::Result<()> { context.join_project_path("ty-override.toml").as_std_path(), r#" [rules] - division-by-zero = "ignore" + literal-math-error = "ignore" "#, )?; @@ -1770,12 +1770,12 @@ fn changes_to_config_file_override() -> anyhow::Result<()> { "Expected no diagnostics but got: {diagnostics:#?}" ); - // Enable division-by-zero in the explicitly specified configuration with warning severity + // Enable literal-math-error in the explicitly specified configuration with warning severity update_file( case.project_path("ty-override.toml"), r#" [rules] - division-by-zero = "warn" + literal-math-error = "warn" "#, )?; diff --git a/crates/ty_project/src/metadata/options.rs b/crates/ty_project/src/metadata/options.rs index e627e01718..98a62dbca0 100644 --- a/crates/ty_project/src/metadata/options.rs +++ b/crates/ty_project/src/metadata/options.rs @@ -67,7 +67,7 @@ pub struct Options { example = r#" [tool.ty.rules] possibly-unresolved-reference = "warn" - division-by-zero = "ignore" + literal-math-error = "ignore" "# )] pub rules: Option, diff --git a/crates/ty_python_semantic/resources/mdtest/binary/booleans.md b/crates/ty_python_semantic/resources/mdtest/binary/booleans.md index e0bcca56e9..5017a93178 100644 --- a/crates/ty_python_semantic/resources/mdtest/binary/booleans.md +++ b/crates/ty_python_semantic/resources/mdtest/binary/booleans.md @@ -37,8 +37,8 @@ reveal_type(b**b) # revealed: Literal[1] # Division reveal_type(a / a) # revealed: float reveal_type(b / a) # revealed: float -b / b # error: [division-by-zero] "Cannot divide object of type `Literal[False]` by zero" -a / b # error: [division-by-zero] "Cannot divide object of type `Literal[True]` by zero" +b / b # error: [literal-math-error] "Cannot divide object of type `Literal[False]` by zero" +a / b # error: [literal-math-error] "Cannot divide object of type `Literal[True]` by zero" # bitwise OR reveal_type(a | a) # revealed: Literal[True] diff --git a/crates/ty_python_semantic/resources/mdtest/binary/integers.md b/crates/ty_python_semantic/resources/mdtest/binary/integers.md index 612f367bcf..c81c8fdb08 100644 --- a/crates/ty_python_semantic/resources/mdtest/binary/integers.md +++ b/crates/ty_python_semantic/resources/mdtest/binary/integers.md @@ -135,9 +135,9 @@ reveal_type(int() / 0) # revealed: int | float # error: "Cannot divide object of type `Literal[1]` by zero" reveal_type(1 / False) # revealed: float -# error: [division-by-zero] "Cannot divide object of type `Literal[True]` by zero" +# error: [literal-math-error] "Cannot divide object of type `Literal[True]` by zero" True / False -# error: [division-by-zero] "Cannot divide object of type `Literal[True]` by zero" +# error: [literal-math-error] "Cannot divide object of type `Literal[True]` by zero" bool(1) / False # error: "Cannot divide object of type `float` by zero" @@ -176,20 +176,21 @@ reveal_type(0 >> 100) # revealed: Literal[0] reveal_type(-42 >> 100) # revealed: Literal[-1] ``` -It is an error to shift by a negative value. This is handled similarly to `division-by-zero`, above: +It is an error to shift by a negative value. This is handled similarly to `ZeroDivisionError` +detection, above: ```py -# error: [negative-shift] "Cannot left shift object of type `Literal[42]` by a negative value" +# error: [literal-math-error] "Cannot left shift object of type `Literal[42]` by a negative value" reveal_type(42 << -3) # revealed: int -# error: [negative-shift] "Cannot left shift object of type `Literal[0]` by a negative value" +# error: [literal-math-error] reveal_type(0 << -3) # revealed: int -# error: [negative-shift] "Cannot left shift object of type `Literal[-42]` by a negative value" +# error: [literal-math-error] reveal_type(-42 << -3) # revealed: int -# error: [negative-shift] "Cannot right shift object of type `Literal[42]` by a negative value" +# error: [literal-math-error] "Cannot right shift object of type `Literal[42]` by a negative value" reveal_type(42 >> -3) # revealed: int -# error: [negative-shift] "Cannot right shift object of type `Literal[0]` by a negative value" +# error: [literal-math-error] reveal_type(0 >> -3) # revealed: int -# error: [negative-shift] "Cannot right shift object of type `Literal[-42]` by a negative value" +# error: [literal-math-error] reveal_type(-42 >> -3) # revealed: int ``` diff --git a/crates/ty_python_semantic/resources/mdtest/binary/unions.md b/crates/ty_python_semantic/resources/mdtest/binary/unions.md index 1ec0794cc4..58c31dd628 100644 --- a/crates/ty_python_semantic/resources/mdtest/binary/unions.md +++ b/crates/ty_python_semantic/resources/mdtest/binary/unions.md @@ -54,6 +54,6 @@ If any of the union elements leads to a division by zero, we will report an erro ```py def f5(m: int, n: Literal[-1, 0, 1]): - # error: [division-by-zero] "Cannot divide object of type `int` by zero" + # error: [literal-math-error] "Cannot divide object of type `int` by zero" return m / n ``` diff --git a/crates/ty_python_semantic/resources/mdtest/suppressions/ty_ignore.md b/crates/ty_python_semantic/resources/mdtest/suppressions/ty_ignore.md index 9a1930f015..3a55491106 100644 --- a/crates/ty_python_semantic/resources/mdtest/suppressions/ty_ignore.md +++ b/crates/ty_python_semantic/resources/mdtest/suppressions/ty_ignore.md @@ -34,9 +34,9 @@ a = test + 3 # ty: ignore[possibly-unresolved-reference] ```py # error: [unused-ignore-comment] -a = 10 / 2 # ty: ignore[division-by-zero] -a = 10 / 2 # ty: ignore[division-by-zero, unused-ignore-comment] -a = 10 / 2 # ty: ignore[unused-ignore-comment, division-by-zero] +a = 10 / 2 # ty: ignore[literal-math-error] +a = 10 / 2 # ty: ignore[literal-math-error, unused-ignore-comment] +a = 10 / 2 # ty: ignore[unused-ignore-comment, literal-math-error] a = 10 / 2 # ty: ignore[unused-ignore-comment] # type: ignore a = 10 / 2 # type: ignore # ty: ignore[unused-ignore-comment] ``` @@ -45,7 +45,7 @@ a = 10 / 2 # type: ignore # ty: ignore[unused-ignore-comment] ```py # error: [unused-ignore-comment] "Unused `ty: ignore` directive: 'unused-ignore-comment'" -a = 10 / 0 # ty: ignore[division-by-zero, unused-ignore-comment] +a = 10 / 0 # ty: ignore[literal-math-error, unused-ignore-comment] ``` ## Multiple unused comments @@ -54,13 +54,13 @@ Today, ty emits a diagnostic for every unused code. We might want to group the c some point in the future. ```py -# error: [unused-ignore-comment] "Unused `ty: ignore` directive: 'division-by-zero'" +# error: [unused-ignore-comment] "Unused `ty: ignore` directive: 'literal-math-error'" # error: [unused-ignore-comment] "Unused `ty: ignore` directive: 'unresolved-reference'" -a = 10 / 2 # ty: ignore[division-by-zero, unresolved-reference] +a = 10 / 2 # ty: ignore[literal-math-error, unresolved-reference] # error: [unused-ignore-comment] "Unused `ty: ignore` directive: 'invalid-assignment'" # error: [unused-ignore-comment] "Unused `ty: ignore` directive: 'unresolved-reference'" -a = 10 / 0 # ty: ignore[invalid-assignment, division-by-zero, unresolved-reference] +a = 10 / 0 # ty: ignore[invalid-assignment, literal-math-error, unresolved-reference] ``` ## Multiple suppressions @@ -96,14 +96,14 @@ reveal_type(a) # ty: ignore[revealed-type] ```py a = 10 / 0 # ty : ignore -a = 10 / 0 # ty: ignore [ division-by-zero ] +a = 10 / 0 # ty: ignore [ literal-math-error ] ``` ## Whitespace is optional ```py # fmt: off -a = 10 / 0 #ty:ignore[division-by-zero] +a = 10 / 0 #ty:ignore[literal-math-error] ``` ## Trailing codes comma @@ -111,13 +111,13 @@ a = 10 / 0 #ty:ignore[division-by-zero] Trailing commas in the codes section are allowed: ```py -a = 10 / 0 # ty: ignore[division-by-zero,] +a = 10 / 0 # ty: ignore[literal-math-error,] ``` ## Invalid characters in codes ```py -# error: [division-by-zero] +# error: [literal-math-error] # error: [invalid-ignore-comment] "Invalid `ty: ignore` comment: expected a alphanumeric character or `-` or `_` as code" a = 10 / 0 # ty: ignore[*-*] ``` @@ -127,7 +127,7 @@ a = 10 / 0 # ty: ignore[*-*] ```py -a = 10 / 0 # ty: ignore[division-by-zero] +a = 10 / 0 # ty: ignore[literal-math-error] # ^^^^^^ trailing whitespace ``` @@ -141,7 +141,7 @@ future. ```py # error: [unresolved-reference] # error: [invalid-ignore-comment] "Invalid `ty: ignore` comment: expected a comma separating the rule codes" -a = x / 0 # ty: ignore[division-by-zero unresolved-reference] +a = x / 0 # ty: ignore[literal-math-error unresolved-reference] ``` ## Missing closing bracket @@ -157,7 +157,7 @@ a = x / 2 # ty: ignore[unresolved-reference An empty codes array suppresses no-diagnostics and is always useless ```py -# error: [division-by-zero] +# error: [literal-math-error] # error: [unused-ignore-comment] "Unused `ty: ignore` without a code" a = 4 / 0 # ty: ignore[] ``` @@ -170,9 +170,9 @@ severity: `ty: possibly-undefined-reference=error` ```py # error: [unused-ignore-comment] -# ty: ignore[division-by-zero] +# ty: ignore[literal-math-error] -a = 4 / 0 # error: [division-by-zero] +a = 4 / 0 # error: [literal-math-error] ``` ## Unknown rule @@ -185,7 +185,7 @@ a = 10 + 4 # ty: ignore[is-equal-14] ## Code with `lint:` prefix ```py -# error:[unknown-rule] "Unknown rule `lint:division-by-zero`. Did you mean `division-by-zero`?" -# error: [division-by-zero] -a = 10 / 0 # ty: ignore[lint:division-by-zero] +# error:[unknown-rule] "Unknown rule `lint:literal-math-error`. Did you mean `literal-math-error`?" +# error: [literal-math-error] +a = 10 / 0 # ty: ignore[lint:literal-math-error] ``` diff --git a/crates/ty_python_semantic/resources/mdtest/suppressions/type_ignore.md b/crates/ty_python_semantic/resources/mdtest/suppressions/type_ignore.md index 43e9eedcc2..a261c12d3f 100644 --- a/crates/ty_python_semantic/resources/mdtest/suppressions/type_ignore.md +++ b/crates/ty_python_semantic/resources/mdtest/suppressions/type_ignore.md @@ -156,6 +156,6 @@ including module docstrings. # error: [unused-ignore-comment] "Unused blanket `type: ignore` directive" # type: ignore -a = 10 / 0 # error: [division-by-zero] -b = a / 0 # error: [division-by-zero] +a = 10 / 0 # error: [literal-math-error] +b = a / 0 # error: [literal-math-error] ``` diff --git a/crates/ty_python_semantic/resources/mdtest/unreachable.md b/crates/ty_python_semantic/resources/mdtest/unreachable.md index 2d01b4dbbf..c5742893b5 100644 --- a/crates/ty_python_semantic/resources/mdtest/unreachable.md +++ b/crates/ty_python_semantic/resources/mdtest/unreachable.md @@ -520,7 +520,7 @@ if False: def f(): return - 1 / 0 # error: [division-by-zero] + 1 / 0 # error: [literal-math-error] ``` ### Conflicting type information diff --git a/crates/ty_python_semantic/src/suppression.rs b/crates/ty_python_semantic/src/suppression.rs index cacdc41ec3..cc45bbf151 100644 --- a/crates/ty_python_semantic/src/suppression.rs +++ b/crates/ty_python_semantic/src/suppression.rs @@ -22,7 +22,7 @@ declare_lint! { /// /// ## Examples /// ```py - /// a = 20 / 2 # ty: ignore[division-by-zero] + /// a = 20 / 2 # ty: ignore[literal-math-error] /// ``` /// /// Use instead: @@ -53,7 +53,7 @@ declare_lint! { /// Use instead: /// /// ```py - /// a = 20 / 0 # ty: ignore[division-by-zero] + /// a = 20 / 0 # ty: ignore[literal-math-error] /// ``` pub(crate) static UNKNOWN_RULE = { summary: "detects `ty: ignore` comments that reference unknown rules", @@ -241,7 +241,7 @@ fn check_unused_suppressions(context: &mut CheckSuppressionsContext) { // This looks silly but it's necessary to check again if a `unused-ignore-comment` is indeed unused // in case the "unused" directive comes after it: // ```py - // a = 10 / 2 # ty: ignore[unused-ignore-comment, division-by-zero] + // a = 10 / 2 # ty: ignore[unused-ignore-comment, literal-math-error] // ``` if context.diagnostics.is_used(suppression.id()) { continue; diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs index bf42406a7f..ce1c1c406e 100644 --- a/crates/ty_python_semantic/src/types/diagnostic.rs +++ b/crates/ty_python_semantic/src/types/diagnostic.rs @@ -34,7 +34,7 @@ pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) { registry.register_lint(&CONFLICTING_DECLARATIONS); registry.register_lint(&CONFLICTING_METACLASS); registry.register_lint(&CYCLIC_CLASS_DEFINITION); - registry.register_lint(&DIVISION_BY_ZERO); + registry.register_lint(&LITERAL_MATH_ERROR); registry.register_lint(&DUPLICATE_BASE); registry.register_lint(&DUPLICATE_KW_ONLY); registry.register_lint(&INSTANCE_LAYOUT_CONFLICT); @@ -62,7 +62,6 @@ pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) { registry.register_lint(&INVALID_TYPE_GUARD_CALL); registry.register_lint(&INVALID_TYPE_VARIABLE_CONSTRAINTS); registry.register_lint(&MISSING_ARGUMENT); - registry.register_lint(&NEGATIVE_SHIFT); registry.register_lint(&NO_MATCHING_OVERLOAD); registry.register_lint(&NON_SUBSCRIPTABLE); registry.register_lint(&NOT_ITERABLE); @@ -245,17 +244,20 @@ declare_lint! { declare_lint! { /// ## What it does - /// It detects division by zero. + /// Detects runtime errors that would result from invalid math operations + /// between two objects with literal `int` types. Examples include division + /// by zero and negative bitshifts. /// /// ## Why is this bad? - /// Dividing by zero raises a `ZeroDivisionError` at runtime. + /// These math operations will lead to exceptions being raised at runtime. /// /// ## Examples /// ```python - /// 5 / 0 + /// 5 / 0 # `ZeroDivisionError` + /// 1 << -1 # `ValueError: negative shift count` /// ``` - pub(crate) static DIVISION_BY_ZERO = { - summary: "detects division by zero", + pub(crate) static LITERAL_MATH_ERROR = { + summary: "detects runtime errors such as division by zero or negative bitshifts", status: LintStatus::preview("1.0.0"), default_level: Level::Ignore, } @@ -1060,25 +1062,6 @@ declare_lint! { } } -declare_lint! { - /// ## What it does - /// Detects shifting an int by a negative value. - /// - /// ## Why is this bad? - /// Shifting an int by a negative value raises a `ValueError` at runtime. - /// - /// ## Examples - /// ```python - /// 42 >> -1 - /// 42 << -1 - /// ``` - pub(crate) static NEGATIVE_SHIFT = { - summary: "detects shifting an int by a negative value", - status: LintStatus::preview("1.0.0"), - default_level: Level::Error, - } -} - declare_lint! { /// ## What it does /// Checks for calls to an overloaded function that do not match any of the overloads. diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index 18c895c3a4..e3b384d8f9 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -89,10 +89,10 @@ use crate::types::call::{Binding, Bindings, CallArgumentTypes, CallArguments, Ca use crate::types::class::{CodeGeneratorKind, MetaclassErrorKind, SliceLiteral}; use crate::types::diagnostic::{ self, CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS, - CYCLIC_CLASS_DEFINITION, DIVISION_BY_ZERO, DUPLICATE_KW_ONLY, INCONSISTENT_MRO, - INVALID_ARGUMENT_TYPE, INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE, - INVALID_DECLARATION, INVALID_GENERIC_CLASS, INVALID_PARAMETER_DEFAULT, INVALID_TYPE_FORM, - INVALID_TYPE_GUARD_CALL, INVALID_TYPE_VARIABLE_CONSTRAINTS, IncompatibleBases, NEGATIVE_SHIFT, + CYCLIC_CLASS_DEFINITION, DUPLICATE_KW_ONLY, INCONSISTENT_MRO, INVALID_ARGUMENT_TYPE, + INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE, INVALID_DECLARATION, + INVALID_GENERIC_CLASS, INVALID_PARAMETER_DEFAULT, INVALID_TYPE_FORM, INVALID_TYPE_GUARD_CALL, + INVALID_TYPE_VARIABLE_CONSTRAINTS, IncompatibleBases, LITERAL_MATH_ERROR, POSSIBLY_UNBOUND_IMPLICIT_CALL, POSSIBLY_UNBOUND_IMPORT, TypeCheckDiagnostics, UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_IMPORT, UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, report_implicit_return_type, report_instance_layout_conflict, @@ -1512,7 +1512,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { } } - /// Raise a diagnostic if the given type cannot be divided by zero, or is shifted by a negative + /// Emit a diagnostic if the given type cannot be divided by zero, or is shifted by a negative /// value. /// /// Expects the resolved type of the left side of the binary expression. @@ -1535,18 +1535,16 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { _ => return false, }; - let (op, by_what, lint) = match (op, lhs_int) { - (ast::Operator::Div, _) => ("divide", "by zero", &DIVISION_BY_ZERO), - (ast::Operator::FloorDiv, _) => ("floor divide", "by zero", &DIVISION_BY_ZERO), - (ast::Operator::Mod, _) => ("reduce", "modulo zero", &DIVISION_BY_ZERO), - (ast::Operator::LShift, true) => ("left shift", "by a negative value", &NEGATIVE_SHIFT), - (ast::Operator::RShift, true) => { - ("right shift", "by a negative value", &NEGATIVE_SHIFT) - } + let (op, by_what) = match (op, lhs_int) { + (ast::Operator::Div, _) => ("divide", "by zero"), + (ast::Operator::FloorDiv, _) => ("floor divide", "by zero"), + (ast::Operator::Mod, _) => ("reduce", "modulo zero"), + (ast::Operator::LShift, true) => ("left shift", "by a negative value"), + (ast::Operator::RShift, true) => ("right shift", "by a negative value"), _ => return false, }; - if let Some(builder) = self.context.report_lint(lint, node) { + if let Some(builder) = self.context.report_lint(&LITERAL_MATH_ERROR, node) { builder.into_diagnostic(format_args!( "Cannot {op} object of type `{}` {by_what}", left.display(self.db()) diff --git a/ty.schema.json b/ty.schema.json index 55f9827644..d4f7f465ef 100644 --- a/ty.schema.json +++ b/ty.schema.json @@ -331,16 +331,6 @@ } ] }, - "division-by-zero": { - "title": "detects division by zero", - "description": "## What it does\nIt detects division by zero.\n\n## Why is this bad?\nDividing by zero raises a `ZeroDivisionError` at runtime.\n\n## Examples\n```python\n5 / 0\n```", - "default": "ignore", - "oneOf": [ - { - "$ref": "#/definitions/Level" - } - ] - }, "duplicate-base": { "title": "detects class definitions with duplicate bases", "description": "## What it does\nChecks for class definitions with duplicate bases.\n\n## Why is this bad?\nClass definitions with duplicate bases raise `TypeError` at runtime.\n\n## Examples\n```python\nclass A: ...\n\n# TypeError: duplicate base class\nclass B(A, A): ...\n```", @@ -661,19 +651,19 @@ } ] }, - "missing-argument": { - "title": "detects missing required arguments in a call", - "description": "## What it does\nChecks for missing required arguments in a call.\n\n## Why is this bad?\nFailing to provide a required argument will raise a `TypeError` at runtime.\n\n## Examples\n```python\ndef func(x: int): ...\nfunc() # TypeError: func() missing 1 required positional argument: 'x'\n```", - "default": "error", + "literal-math-error": { + "title": "detects runtime errors such as division by zero or negative bitshifts", + "description": "## What it does\nDetects runtime errors that would result from invalid math operations\nbetween two objects with literal `int` types. Examples include division\nby zero and negative bitshifts.\n\n## Why is this bad?\nThese math operations will lead to exceptions being raised at runtime.\n\n## Examples\n```python\n5 / 0 # `ZeroDivisionError`\n1 << -1 # `ValueError: negative shift count`\n```", + "default": "ignore", "oneOf": [ { "$ref": "#/definitions/Level" } ] }, - "negative-shift": { - "title": "detects shifting an int by a negative value", - "description": "## What it does\nDetects shifting an int by a negative value.\n\n## Why is this bad?\nShifting an int by a negative value raises a `ValueError` at runtime.\n\n## Examples\n```python\n42 >> -1\n42 << -1\n```", + "missing-argument": { + "title": "detects missing required arguments in a call", + "description": "## What it does\nChecks for missing required arguments in a call.\n\n## Why is this bad?\nFailing to provide a required argument will raise a `TypeError` at runtime.\n\n## Examples\n```python\ndef func(x: int): ...\nfunc() # TypeError: func() missing 1 required positional argument: 'x'\n```", "default": "error", "oneOf": [ { @@ -853,7 +843,7 @@ }, "unknown-rule": { "title": "detects `ty: ignore` comments that reference unknown rules", - "description": "## What it does\nChecks for `ty: ignore[code]` where `code` isn't a known lint rule.\n\n## Why is this bad?\nA `ty: ignore[code]` directive with a `code` that doesn't match\nany known rule will not suppress any type errors, and is probably a mistake.\n\n## Examples\n```py\na = 20 / 0 # ty: ignore[division-by-zer]\n```\n\nUse instead:\n\n```py\na = 20 / 0 # ty: ignore[division-by-zero]\n```", + "description": "## What it does\nChecks for `ty: ignore[code]` where `code` isn't a known lint rule.\n\n## Why is this bad?\nA `ty: ignore[code]` directive with a `code` that doesn't match\nany known rule will not suppress any type errors, and is probably a mistake.\n\n## Examples\n```py\na = 20 / 0 # ty: ignore[division-by-zer]\n```\n\nUse instead:\n\n```py\na = 20 / 0 # ty: ignore[literal-math-error]\n```", "default": "warn", "oneOf": [ { @@ -923,7 +913,7 @@ }, "unused-ignore-comment": { "title": "detects unused `type: ignore` comments", - "description": "## What it does\nChecks for `type: ignore` or `ty: ignore` directives that are no longer applicable.\n\n## Why is this bad?\nA `type: ignore` directive that no longer matches any diagnostic violations is likely\nincluded by mistake, and should be removed to avoid confusion.\n\n## Examples\n```py\na = 20 / 2 # ty: ignore[division-by-zero]\n```\n\nUse instead:\n\n```py\na = 20 / 2\n```", + "description": "## What it does\nChecks for `type: ignore` or `ty: ignore` directives that are no longer applicable.\n\n## Why is this bad?\nA `type: ignore` directive that no longer matches any diagnostic violations is likely\nincluded by mistake, and should be removed to avoid confusion.\n\n## Examples\n```py\na = 20 / 2 # ty: ignore[literal-math-error]\n```\n\nUse instead:\n\n```py\na = 20 / 2\n```", "default": "ignore", "oneOf": [ {