mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:44:56 +00:00
[ty] highlight the argument in static_assert
error messages (#19426)
Closes https://github.com/astral-sh/ty/issues/209. Before: ``` error[static-assert-error]: Static assertion error: custom message --> test.py:2:1 | 1 | from ty_extensions import static_assert 2 | static_assert(3 > 4, "custom message") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ``` After: ``` error[static-assert-error]: Static assertion error: custom message --> test.py:2:1 | 1 | from ty_extensions import static_assert 2 | static_assert(3 > 4, "custom message") | ^^^^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^ | | | Inferred type of argument is `Literal[False]` | ```
This commit is contained in:
parent
5a55bab3f3
commit
88bd82938f
5 changed files with 193 additions and 6 deletions
|
@ -1,5 +1,7 @@
|
||||||
# `cast`
|
# `cast`
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
|
||||||
`cast()` takes two arguments, one type and one value, and returns a value of the given type.
|
`cast()` takes two arguments, one type and one value, and returns a value of the given type.
|
||||||
|
|
||||||
The (inferred) type of the value and the given type do not need to have any correlation.
|
The (inferred) type of the value and the given type do not need to have any correlation.
|
||||||
|
@ -78,3 +80,15 @@ def f(x: Any, y: Unknown, z: Any | str | int):
|
||||||
|
|
||||||
e = cast(str | int | Any, z) # error: [redundant-cast]
|
e = cast(str | int | Any, z) # error: [redundant-cast]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Diagnostic snapshots
|
||||||
|
|
||||||
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
|
```py
|
||||||
|
import secrets
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
|
# error: [redundant-cast] "Value is already of type `int`"
|
||||||
|
cast(int, secrets.randbelow(10))
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
---
|
||||||
|
source: crates/ty_test/src/lib.rs
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
---
|
||||||
|
mdtest name: cast.md - `cast` - Diagnostic snapshots
|
||||||
|
mdtest path: crates/ty_python_semantic/resources/mdtest/directives/cast.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# Python source files
|
||||||
|
|
||||||
|
## mdtest_snippet.py
|
||||||
|
|
||||||
|
```
|
||||||
|
1 | import secrets
|
||||||
|
2 | from typing import cast
|
||||||
|
3 |
|
||||||
|
4 | # error: [redundant-cast] "Value is already of type `int`"
|
||||||
|
5 | cast(int, secrets.randbelow(10))
|
||||||
|
```
|
||||||
|
|
||||||
|
# Diagnostics
|
||||||
|
|
||||||
|
```
|
||||||
|
warning[redundant-cast]: Value is already of type `int`
|
||||||
|
--> src/mdtest_snippet.py:5:1
|
||||||
|
|
|
||||||
|
4 | # error: [redundant-cast] "Value is already of type `int`"
|
||||||
|
5 | cast(int, secrets.randbelow(10))
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
info: rule `redundant-cast` is enabled by default
|
||||||
|
|
||||||
|
```
|
|
@ -0,0 +1,104 @@
|
||||||
|
---
|
||||||
|
source: crates/ty_test/src/lib.rs
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
---
|
||||||
|
mdtest name: type_api.md - Type API (`ty_extensions`) - Diagnostic snapshots
|
||||||
|
mdtest path: crates/ty_python_semantic/resources/mdtest/type_api.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# Python source files
|
||||||
|
|
||||||
|
## mdtest_snippet.py
|
||||||
|
|
||||||
|
```
|
||||||
|
1 | from ty_extensions import static_assert
|
||||||
|
2 | import secrets
|
||||||
|
3 |
|
||||||
|
4 | # a passing assert
|
||||||
|
5 | static_assert(1 < 2)
|
||||||
|
6 |
|
||||||
|
7 | # evaluates to False
|
||||||
|
8 | # error: [static-assert-error]
|
||||||
|
9 | static_assert(1 > 2)
|
||||||
|
10 |
|
||||||
|
11 | # evaluates to False, with a message as the second argument
|
||||||
|
12 | # error: [static-assert-error]
|
||||||
|
13 | static_assert(1 > 2, "with a message")
|
||||||
|
14 |
|
||||||
|
15 | # evaluates to something falsey
|
||||||
|
16 | # error: [static-assert-error]
|
||||||
|
17 | static_assert("")
|
||||||
|
18 |
|
||||||
|
19 | # evaluates to something ambiguous
|
||||||
|
20 | # error: [static-assert-error]
|
||||||
|
21 | static_assert(secrets.randbelow(2))
|
||||||
|
```
|
||||||
|
|
||||||
|
# Diagnostics
|
||||||
|
|
||||||
|
```
|
||||||
|
error[static-assert-error]: Static assertion error: argument evaluates to `False`
|
||||||
|
--> src/mdtest_snippet.py:9:1
|
||||||
|
|
|
||||||
|
7 | # evaluates to False
|
||||||
|
8 | # error: [static-assert-error]
|
||||||
|
9 | static_assert(1 > 2)
|
||||||
|
| ^^^^^^^^^^^^^^-----^
|
||||||
|
| |
|
||||||
|
| Inferred type of argument is `Literal[False]`
|
||||||
|
10 |
|
||||||
|
11 | # evaluates to False, with a message as the second argument
|
||||||
|
|
|
||||||
|
info: rule `static-assert-error` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[static-assert-error]: Static assertion error: with a message
|
||||||
|
--> src/mdtest_snippet.py:13:1
|
||||||
|
|
|
||||||
|
11 | # evaluates to False, with a message as the second argument
|
||||||
|
12 | # error: [static-assert-error]
|
||||||
|
13 | static_assert(1 > 2, "with a message")
|
||||||
|
| ^^^^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| Inferred type of argument is `Literal[False]`
|
||||||
|
14 |
|
||||||
|
15 | # evaluates to something falsey
|
||||||
|
|
|
||||||
|
info: rule `static-assert-error` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[static-assert-error]: Static assertion error: argument of type `Literal[""]` is statically known to be falsy
|
||||||
|
--> src/mdtest_snippet.py:17:1
|
||||||
|
|
|
||||||
|
15 | # evaluates to something falsey
|
||||||
|
16 | # error: [static-assert-error]
|
||||||
|
17 | static_assert("")
|
||||||
|
| ^^^^^^^^^^^^^^--^
|
||||||
|
| |
|
||||||
|
| Inferred type of argument is `Literal[""]`
|
||||||
|
18 |
|
||||||
|
19 | # evaluates to something ambiguous
|
||||||
|
|
|
||||||
|
info: rule `static-assert-error` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[static-assert-error]: Static assertion error: argument of type `int` has an ambiguous static truthiness
|
||||||
|
--> src/mdtest_snippet.py:21:1
|
||||||
|
|
|
||||||
|
19 | # evaluates to something ambiguous
|
||||||
|
20 | # error: [static-assert-error]
|
||||||
|
21 | static_assert(secrets.randbelow(2))
|
||||||
|
| ^^^^^^^^^^^^^^--------------------^
|
||||||
|
| |
|
||||||
|
| Inferred type of argument is `int`
|
||||||
|
|
|
||||||
|
info: rule `static-assert-error` is enabled by default
|
||||||
|
|
||||||
|
```
|
|
@ -266,6 +266,34 @@ shouted_message = "A custom message".upper()
|
||||||
static_assert(False, shouted_message)
|
static_assert(False, shouted_message)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Diagnostic snapshots
|
||||||
|
|
||||||
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
|
```py
|
||||||
|
from ty_extensions import static_assert
|
||||||
|
import secrets
|
||||||
|
|
||||||
|
# a passing assert
|
||||||
|
static_assert(1 < 2)
|
||||||
|
|
||||||
|
# evaluates to False
|
||||||
|
# error: [static-assert-error]
|
||||||
|
static_assert(1 > 2)
|
||||||
|
|
||||||
|
# evaluates to False, with a message as the second argument
|
||||||
|
# error: [static-assert-error]
|
||||||
|
static_assert(1 > 2, "with a message")
|
||||||
|
|
||||||
|
# evaluates to something falsey
|
||||||
|
# error: [static-assert-error]
|
||||||
|
static_assert("")
|
||||||
|
|
||||||
|
# evaluates to something ambiguous
|
||||||
|
# error: [static-assert-error]
|
||||||
|
static_assert(secrets.randbelow(2))
|
||||||
|
```
|
||||||
|
|
||||||
## Type predicates
|
## Type predicates
|
||||||
|
|
||||||
The `ty_extensions` module also provides predicates to test various properties of types. These are
|
The `ty_extensions` module also provides predicates to test various properties of types. These are
|
||||||
|
|
|
@ -1264,28 +1264,35 @@ impl KnownFunction {
|
||||||
if truthiness.is_always_true() {
|
if truthiness.is_always_true() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(message) = message
|
let mut diagnostic = if let Some(message) = message
|
||||||
.and_then(Type::into_string_literal)
|
.and_then(Type::into_string_literal)
|
||||||
.map(|s| s.value(db))
|
.map(|s| s.value(db))
|
||||||
{
|
{
|
||||||
builder.into_diagnostic(format_args!("Static assertion error: {message}"));
|
builder.into_diagnostic(format_args!("Static assertion error: {message}"))
|
||||||
} else if *parameter_ty == Type::BooleanLiteral(false) {
|
} else if *parameter_ty == Type::BooleanLiteral(false) {
|
||||||
builder.into_diagnostic(
|
builder.into_diagnostic(
|
||||||
"Static assertion error: argument evaluates to `False`",
|
"Static assertion error: argument evaluates to `False`",
|
||||||
);
|
)
|
||||||
} else if truthiness.is_always_false() {
|
} else if truthiness.is_always_false() {
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
"Static assertion error: argument of type `{parameter_ty}` \
|
"Static assertion error: argument of type `{parameter_ty}` \
|
||||||
is statically known to be falsy",
|
is statically known to be falsy",
|
||||||
parameter_ty = parameter_ty.display(db)
|
parameter_ty = parameter_ty.display(db)
|
||||||
));
|
))
|
||||||
} else {
|
} else {
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
"Static assertion error: argument of type `{parameter_ty}` \
|
"Static assertion error: argument of type `{parameter_ty}` \
|
||||||
has an ambiguous static truthiness",
|
has an ambiguous static truthiness",
|
||||||
parameter_ty = parameter_ty.display(db)
|
parameter_ty = parameter_ty.display(db)
|
||||||
));
|
))
|
||||||
}
|
};
|
||||||
|
diagnostic.annotate(
|
||||||
|
Annotation::secondary(context.span(&call_expression.arguments.args[0]))
|
||||||
|
.message(format_args!(
|
||||||
|
"Inferred type of argument is `{}`",
|
||||||
|
parameter_ty.display(db)
|
||||||
|
)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue