diff --git a/crates/ty_python_semantic/resources/mdtest/directives/cast.md b/crates/ty_python_semantic/resources/mdtest/directives/cast.md index 6943eceee6..9be0c664d2 100644 --- a/crates/ty_python_semantic/resources/mdtest/directives/cast.md +++ b/crates/ty_python_semantic/resources/mdtest/directives/cast.md @@ -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. @@ -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 + + + +```py +import secrets +from typing import cast + +# error: [redundant-cast] "Value is already of type `int`" +cast(int, secrets.randbelow(10)) +``` diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/cast.md_-_`cast`_-_Diagnostic_snapshots_(91dd3d45b6d7f2c8).snap b/crates/ty_python_semantic/resources/mdtest/snapshots/cast.md_-_`cast`_-_Diagnostic_snapshots_(91dd3d45b6d7f2c8).snap new file mode 100644 index 0000000000..fe3aeb92e7 --- /dev/null +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/cast.md_-_`cast`_-_Diagnostic_snapshots_(91dd3d45b6d7f2c8).snap @@ -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 + +``` diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/type_api.md_-_Type_API_(`ty_extens…_-_Diagnostic_snapshots_(be112fd8b8cfdc28).snap b/crates/ty_python_semantic/resources/mdtest/snapshots/type_api.md_-_Type_API_(`ty_extens…_-_Diagnostic_snapshots_(be112fd8b8cfdc28).snap new file mode 100644 index 0000000000..ad8e363531 --- /dev/null +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/type_api.md_-_Type_API_(`ty_extens…_-_Diagnostic_snapshots_(be112fd8b8cfdc28).snap @@ -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 + +``` diff --git a/crates/ty_python_semantic/resources/mdtest/type_api.md b/crates/ty_python_semantic/resources/mdtest/type_api.md index 6f7c2c8180..1e7dee0a7c 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_api.md +++ b/crates/ty_python_semantic/resources/mdtest/type_api.md @@ -266,6 +266,34 @@ shouted_message = "A custom message".upper() static_assert(False, shouted_message) ``` +## Diagnostic snapshots + + + +```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 diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs index 13e485224b..4d96b61731 100644 --- a/crates/ty_python_semantic/src/types/function.rs +++ b/crates/ty_python_semantic/src/types/function.rs @@ -1264,28 +1264,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) + )), + ); } }