diff --git a/crates/ruff_db/src/diagnostic/mod.rs b/crates/ruff_db/src/diagnostic/mod.rs index 6807f67888..023fc59368 100644 --- a/crates/ruff_db/src/diagnostic/mod.rs +++ b/crates/ruff_db/src/diagnostic/mod.rs @@ -227,6 +227,11 @@ impl Diagnostic { pub fn primary_span(&self) -> Option { self.primary_annotation().map(|ann| ann.span.clone()) } + + /// Returns the tags from the primary annotation of this diagnostic if it exists. + pub fn primary_tags(&self) -> Option<&[DiagnosticTag]> { + self.primary_annotation().map(|ann| ann.tags.as_slice()) + } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -338,6 +343,8 @@ pub struct Annotation { /// Whether this annotation is "primary" or not. When it isn't primary, an /// annotation is said to be "secondary." is_primary: bool, + /// The diagnostic tags associated with this annotation. + tags: Vec, } impl Annotation { @@ -355,6 +362,7 @@ impl Annotation { span, message: None, is_primary: true, + tags: Vec::new(), } } @@ -370,6 +378,7 @@ impl Annotation { span, message: None, is_primary: false, + tags: Vec::new(), } } @@ -412,6 +421,36 @@ impl Annotation { pub fn get_span(&self) -> &Span { &self.span } + + /// Returns the tags associated with this annotation. + pub fn get_tags(&self) -> &[DiagnosticTag] { + &self.tags + } + + /// Attaches this tag to this annotation. + /// + /// It will not replace any existing tags. + pub fn tag(mut self, tag: DiagnosticTag) -> Annotation { + self.tags.push(tag); + self + } + + /// Attaches an additional tag to this annotation. + pub fn push_tag(&mut self, tag: DiagnosticTag) { + self.tags.push(tag); + } +} + +/// Tags that can be associated with an annotation. +/// +/// These tags are used to provide additional information about the annotation. +/// and are passed through to the language server protocol. +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum DiagnosticTag { + /// Unused or unnecessary code. Used for unused parameters, unreachable code, etc. + Unnecessary, + /// Deprecated or obsolete code. + Deprecated, } /// A string identifier for a lint rule. diff --git a/crates/ty_python_semantic/src/types/context.rs b/crates/ty_python_semantic/src/types/context.rs index 37d16c9f22..6c63b529cd 100644 --- a/crates/ty_python_semantic/src/types/context.rs +++ b/crates/ty_python_semantic/src/types/context.rs @@ -1,6 +1,7 @@ use std::fmt; use drop_bomb::DebugDropBomb; +use ruff_db::diagnostic::DiagnosticTag; use ruff_db::{ diagnostic::{Annotation, Diagnostic, DiagnosticId, IntoDiagnosticMessage, Severity, Span}, files::File, @@ -259,6 +260,21 @@ impl LintDiagnosticGuard<'_, '_> { let ann = self.primary_annotation_mut().unwrap(); ann.set_message(message); } + + /// Adds a tag on the primary annotation for this diagnostic. + /// + /// This tag is associated with the primary annotation created + /// for every `Diagnostic` that uses the `LintDiagnosticGuard` API. + /// Specifically, the annotation is derived from the `TextRange` given to + /// the `InferContext::report_lint` API. + /// + /// Callers can add additional primary or secondary annotations via the + /// `DerefMut` trait implementation to a `Diagnostic`. + #[expect(dead_code)] + pub(super) fn add_primary_tag(&mut self, tag: DiagnosticTag) { + let ann = self.primary_annotation_mut().unwrap(); + ann.push_tag(tag); + } } impl std::ops::Deref for LintDiagnosticGuard<'_, '_> { diff --git a/crates/ty_server/src/server/api/requests/diagnostic.rs b/crates/ty_server/src/server/api/requests/diagnostic.rs index e128e22a44..201d5db91b 100644 --- a/crates/ty_server/src/server/api/requests/diagnostic.rs +++ b/crates/ty_server/src/server/api/requests/diagnostic.rs @@ -2,9 +2,9 @@ use std::borrow::Cow; use lsp_types::request::DocumentDiagnosticRequest; use lsp_types::{ - Diagnostic, DiagnosticSeverity, DocumentDiagnosticParams, DocumentDiagnosticReport, - DocumentDiagnosticReportResult, FullDocumentDiagnosticReport, NumberOrString, Range, - RelatedFullDocumentDiagnosticReport, Url, + Diagnostic, DiagnosticSeverity, DiagnosticTag, DocumentDiagnosticParams, + DocumentDiagnosticReport, DocumentDiagnosticReportResult, FullDocumentDiagnosticReport, + NumberOrString, Range, RelatedFullDocumentDiagnosticReport, Url, }; use crate::document::ToRangeExt; @@ -92,10 +92,22 @@ fn to_lsp_diagnostic( Severity::Error | Severity::Fatal => DiagnosticSeverity::ERROR, }; + let tags = diagnostic + .primary_tags() + .map(|tags| { + tags.iter() + .map(|tag| match tag { + ruff_db::diagnostic::DiagnosticTag::Unnecessary => DiagnosticTag::UNNECESSARY, + ruff_db::diagnostic::DiagnosticTag::Deprecated => DiagnosticTag::DEPRECATED, + }) + .collect::>() + }) + .filter(|mapped_tags| !mapped_tags.is_empty()); + Diagnostic { range, severity: Some(severity), - tags: None, + tags, code: Some(NumberOrString::String(diagnostic.id().to_string())), code_description: None, source: Some("ty".into()),