Skip to content

Commit 88bd829

Browse files
authored
[ty] highlight the argument in static_assert error messages (#19426)
Closes astral-sh/ty#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]` | ```
1 parent 5a55bab commit 88bd829

File tree

5 files changed

+193
-6
lines changed

5 files changed

+193
-6
lines changed

crates/ty_python_semantic/resources/mdtest/directives/cast.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# `cast`
22

3+
## Behavior
4+
35
`cast()` takes two arguments, one type and one value, and returns a value of the given type.
46

57
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):
7880

7981
e = cast(str | int | Any, z) # error: [redundant-cast]
8082
```
83+
84+
## Diagnostic snapshots
85+
86+
<!-- snapshot-diagnostics -->
87+
88+
```py
89+
import secrets
90+
from typing import cast
91+
92+
# error: [redundant-cast] "Value is already of type `int`"
93+
cast(int, secrets.randbelow(10))
94+
```
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
source: crates/ty_test/src/lib.rs
3+
expression: snapshot
4+
---
5+
---
6+
mdtest name: cast.md - `cast` - Diagnostic snapshots
7+
mdtest path: crates/ty_python_semantic/resources/mdtest/directives/cast.md
8+
---
9+
10+
# Python source files
11+
12+
## mdtest_snippet.py
13+
14+
```
15+
1 | import secrets
16+
2 | from typing import cast
17+
3 |
18+
4 | # error: [redundant-cast] "Value is already of type `int`"
19+
5 | cast(int, secrets.randbelow(10))
20+
```
21+
22+
# Diagnostics
23+
24+
```
25+
warning[redundant-cast]: Value is already of type `int`
26+
--> src/mdtest_snippet.py:5:1
27+
|
28+
4 | # error: [redundant-cast] "Value is already of type `int`"
29+
5 | cast(int, secrets.randbelow(10))
30+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
31+
|
32+
info: rule `redundant-cast` is enabled by default
33+
34+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
source: crates/ty_test/src/lib.rs
3+
expression: snapshot
4+
---
5+
---
6+
mdtest name: type_api.md - Type API (`ty_extensions`) - Diagnostic snapshots
7+
mdtest path: crates/ty_python_semantic/resources/mdtest/type_api.md
8+
---
9+
10+
# Python source files
11+
12+
## mdtest_snippet.py
13+
14+
```
15+
1 | from ty_extensions import static_assert
16+
2 | import secrets
17+
3 |
18+
4 | # a passing assert
19+
5 | static_assert(1 < 2)
20+
6 |
21+
7 | # evaluates to False
22+
8 | # error: [static-assert-error]
23+
9 | static_assert(1 > 2)
24+
10 |
25+
11 | # evaluates to False, with a message as the second argument
26+
12 | # error: [static-assert-error]
27+
13 | static_assert(1 > 2, "with a message")
28+
14 |
29+
15 | # evaluates to something falsey
30+
16 | # error: [static-assert-error]
31+
17 | static_assert("")
32+
18 |
33+
19 | # evaluates to something ambiguous
34+
20 | # error: [static-assert-error]
35+
21 | static_assert(secrets.randbelow(2))
36+
```
37+
38+
# Diagnostics
39+
40+
```
41+
error[static-assert-error]: Static assertion error: argument evaluates to `False`
42+
--> src/mdtest_snippet.py:9:1
43+
|
44+
7 | # evaluates to False
45+
8 | # error: [static-assert-error]
46+
9 | static_assert(1 > 2)
47+
| ^^^^^^^^^^^^^^-----^
48+
| |
49+
| Inferred type of argument is `Literal[False]`
50+
10 |
51+
11 | # evaluates to False, with a message as the second argument
52+
|
53+
info: rule `static-assert-error` is enabled by default
54+
55+
```
56+
57+
```
58+
error[static-assert-error]: Static assertion error: with a message
59+
--> src/mdtest_snippet.py:13:1
60+
|
61+
11 | # evaluates to False, with a message as the second argument
62+
12 | # error: [static-assert-error]
63+
13 | static_assert(1 > 2, "with a message")
64+
| ^^^^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^
65+
| |
66+
| Inferred type of argument is `Literal[False]`
67+
14 |
68+
15 | # evaluates to something falsey
69+
|
70+
info: rule `static-assert-error` is enabled by default
71+
72+
```
73+
74+
```
75+
error[static-assert-error]: Static assertion error: argument of type `Literal[""]` is statically known to be falsy
76+
--> src/mdtest_snippet.py:17:1
77+
|
78+
15 | # evaluates to something falsey
79+
16 | # error: [static-assert-error]
80+
17 | static_assert("")
81+
| ^^^^^^^^^^^^^^--^
82+
| |
83+
| Inferred type of argument is `Literal[""]`
84+
18 |
85+
19 | # evaluates to something ambiguous
86+
|
87+
info: rule `static-assert-error` is enabled by default
88+
89+
```
90+
91+
```
92+
error[static-assert-error]: Static assertion error: argument of type `int` has an ambiguous static truthiness
93+
--> src/mdtest_snippet.py:21:1
94+
|
95+
19 | # evaluates to something ambiguous
96+
20 | # error: [static-assert-error]
97+
21 | static_assert(secrets.randbelow(2))
98+
| ^^^^^^^^^^^^^^--------------------^
99+
| |
100+
| Inferred type of argument is `int`
101+
|
102+
info: rule `static-assert-error` is enabled by default
103+
104+
```

crates/ty_python_semantic/resources/mdtest/type_api.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,34 @@ shouted_message = "A custom message".upper()
266266
static_assert(False, shouted_message)
267267
```
268268

269+
## Diagnostic snapshots
270+
271+
<!-- snapshot-diagnostics -->
272+
273+
```py
274+
from ty_extensions import static_assert
275+
import secrets
276+
277+
# a passing assert
278+
static_assert(1 < 2)
279+
280+
# evaluates to False
281+
# error: [static-assert-error]
282+
static_assert(1 > 2)
283+
284+
# evaluates to False, with a message as the second argument
285+
# error: [static-assert-error]
286+
static_assert(1 > 2, "with a message")
287+
288+
# evaluates to something falsey
289+
# error: [static-assert-error]
290+
static_assert("")
291+
292+
# evaluates to something ambiguous
293+
# error: [static-assert-error]
294+
static_assert(secrets.randbelow(2))
295+
```
296+
269297
## Type predicates
270298

271299
The `ty_extensions` module also provides predicates to test various properties of types. These are

crates/ty_python_semantic/src/types/function.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,28 +1264,35 @@ impl KnownFunction {
12641264
if truthiness.is_always_true() {
12651265
return;
12661266
}
1267-
if let Some(message) = message
1267+
let mut diagnostic = if let Some(message) = message
12681268
.and_then(Type::into_string_literal)
12691269
.map(|s| s.value(db))
12701270
{
1271-
builder.into_diagnostic(format_args!("Static assertion error: {message}"));
1271+
builder.into_diagnostic(format_args!("Static assertion error: {message}"))
12721272
} else if *parameter_ty == Type::BooleanLiteral(false) {
12731273
builder.into_diagnostic(
12741274
"Static assertion error: argument evaluates to `False`",
1275-
);
1275+
)
12761276
} else if truthiness.is_always_false() {
12771277
builder.into_diagnostic(format_args!(
12781278
"Static assertion error: argument of type `{parameter_ty}` \
12791279
is statically known to be falsy",
12801280
parameter_ty = parameter_ty.display(db)
1281-
));
1281+
))
12821282
} else {
12831283
builder.into_diagnostic(format_args!(
12841284
"Static assertion error: argument of type `{parameter_ty}` \
12851285
has an ambiguous static truthiness",
12861286
parameter_ty = parameter_ty.display(db)
1287-
));
1288-
}
1287+
))
1288+
};
1289+
diagnostic.annotate(
1290+
Annotation::secondary(context.span(&call_expression.arguments.args[0]))
1291+
.message(format_args!(
1292+
"Inferred type of argument is `{}`",
1293+
parameter_ty.display(db)
1294+
)),
1295+
);
12891296
}
12901297
}
12911298

0 commit comments

Comments
 (0)