[ty] Add type-expression syntax link to invalid-type-expression (#18104)

## Summary

Add a link to [this
page](https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions)
when emitting `invalid-type-expression` diagnostics.
This commit is contained in:
David Peter 2025-05-14 20:56:44 +02:00 committed by GitHub
parent 68559fc17d
commit 6800a9f6f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 72 additions and 59 deletions

View file

@ -9,6 +9,7 @@ use crate::types::string_annotation::{
IMPLICIT_CONCATENATED_STRING_TYPE_ANNOTATION, INVALID_SYNTAX_IN_FORWARD_ANNOTATION,
RAW_STRING_TYPE_ANNOTATION,
};
use crate::types::LintDiagnosticGuard;
use crate::types::{protocol_class::ProtocolClassLiteral, KnownFunction, KnownInstanceType, Type};
use crate::{declare_lint, Program};
use ruff_db::diagnostic::{Annotation, Diagnostic, Severity, SubDiagnostic};
@ -788,7 +789,7 @@ declare_lint! {
declare_lint! {
/// ## What it does
/// Checks for expressions that are used as type expressions
/// Checks for expressions that are used as [type expressions]
/// but cannot validly be interpreted as such.
///
/// ## Why is this bad?
@ -802,6 +803,7 @@ declare_lint! {
/// a: type[1] # `1` is not a type
/// b: Annotated[int] # `Annotated` expects at least two arguments
/// ```
/// [type expressions]: https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions
pub(crate) static INVALID_TYPE_FORM = {
summary: "detects invalid type forms",
status: LintStatus::preview("1.0.0"),
@ -1774,6 +1776,13 @@ pub(crate) fn report_invalid_arguments_to_callable(
));
}
pub(crate) fn add_type_expression_reference_link(mut diag: LintDiagnosticGuard) {
diag.info("See the following page for a reference on valid type expressions:");
diag.info(
"https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions",
);
}
pub(crate) fn report_runtime_check_against_non_runtime_checkable_protocol(
context: &InferContext,
call: &ast::ExprCall,

View file

@ -68,7 +68,7 @@ use crate::symbol::{
use crate::types::call::{Argument, Bindings, CallArgumentTypes, CallArguments, CallError};
use crate::types::class::{MetaclassErrorKind, SliceLiteral};
use crate::types::diagnostic::{
report_implicit_return_type, report_invalid_arguments_to_annotated,
self, report_implicit_return_type, report_invalid_arguments_to_annotated,
report_invalid_arguments_to_callable, report_invalid_assignment,
report_invalid_attribute_assignment, report_invalid_generator_function_return_type,
report_invalid_return_type, report_possibly_unbound_attribute, TypeCheckDiagnostics,
@ -7743,7 +7743,8 @@ impl<'db> TypeInferenceBuilder<'db> {
message: std::fmt::Arguments,
) -> Type<'db> {
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, expression) {
builder.into_diagnostic(message);
let diag = builder.into_diagnostic(message);
diagnostic::add_type_expression_reference_link(diag);
}
Type::unknown()
}
@ -8571,11 +8572,12 @@ impl<'db> TypeInferenceBuilder<'db> {
}
KnownInstanceType::ClassVar | KnownInstanceType::Final => {
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
builder.into_diagnostic(format_args!(
let diag = builder.into_diagnostic(format_args!(
"Type qualifier `{}` is not allowed in type expressions \
(only in annotation expressions)",
known_instance.repr(self.db())
));
diagnostic::add_type_expression_reference_link(diag);
}
self.infer_type_expression(arguments_slice)
}
@ -8799,11 +8801,12 @@ impl<'db> TypeInferenceBuilder<'db> {
}
_ => {
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, parameters) {
builder.into_diagnostic(format_args!(
let diag = builder.into_diagnostic(format_args!(
"The first argument to `Callable` \
must be either a list of types, \
ParamSpec, Concatenate, or `...`",
));
diagnostic::add_type_expression_reference_link(diag);
}
return None;
}