Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions crates/ty_python_semantic/resources/mdtest/directives/cast.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# `cast`

## Behavior

`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.
Expand Down Expand Up @@ -78,3 +80,15 @@ def f(x: Any, y: Unknown, z: Any | str | int):

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))
```
Original file line number Diff line number Diff line change
@@ -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

```
Original file line number Diff line number Diff line change
@@ -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

```
28 changes: 28 additions & 0 deletions crates/ty_python_semantic/resources/mdtest/type_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,34 @@ shouted_message = "A custom message".upper()
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

The `ty_extensions` module also provides predicates to test various properties of types. These are
Expand Down
19 changes: 13 additions & 6 deletions crates/ty_python_semantic/src/types/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1151,28 +1151,35 @@ impl KnownFunction {
if truthiness.is_always_true() {
return;
}
if let Some(message) = message
let mut diagnostic = if let Some(message) = message
.and_then(Type::into_string_literal)
.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) {
builder.into_diagnostic(
"Static assertion error: argument evaluates to `False`",
);
)
} else if truthiness.is_always_false() {
builder.into_diagnostic(format_args!(
"Static assertion error: argument of type `{parameter_ty}` \
is statically known to be falsy",
parameter_ty = parameter_ty.display(db)
));
))
} else {
builder.into_diagnostic(format_args!(
"Static assertion error: argument of type `{parameter_ty}` \
has an ambiguous static truthiness",
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)
)),
);
}
}

Expand Down
Loading