diff --git a/crates/red_knot_project/src/lib.rs b/crates/red_knot_project/src/lib.rs index 7268745457..411be2230f 100644 --- a/crates/red_knot_project/src/lib.rs +++ b/crates/red_knot_project/src/lib.rs @@ -402,8 +402,8 @@ impl Diagnostic for IOErrorDiagnostic { self.error.to_string().into() } - fn file(&self) -> File { - self.file + fn file(&self) -> Option { + Some(self.file) } fn range(&self) -> Option { diff --git a/crates/red_knot_project/src/metadata/options.rs b/crates/red_knot_project/src/metadata/options.rs index 283fca9b94..edaccf5e17 100644 --- a/crates/red_knot_project/src/metadata/options.rs +++ b/crates/red_knot_project/src/metadata/options.rs @@ -5,6 +5,7 @@ use red_knot_python_semantic::{ use ruff_db::system::{System, SystemPath}; use ruff_macros::Combine; use serde::{Deserialize, Serialize}; +use std::fmt::Debug; use thiserror::Error; /// The options for the project. diff --git a/crates/red_knot_python_semantic/src/types/diagnostic.rs b/crates/red_knot_python_semantic/src/types/diagnostic.rs index a20ae364b1..99d07dd9f9 100644 --- a/crates/red_knot_python_semantic/src/types/diagnostic.rs +++ b/crates/red_knot_python_semantic/src/types/diagnostic.rs @@ -802,8 +802,8 @@ impl Diagnostic for TypeCheckDiagnostic { TypeCheckDiagnostic::message(self).into() } - fn file(&self) -> File { - TypeCheckDiagnostic::file(self) + fn file(&self) -> Option { + Some(TypeCheckDiagnostic::file(self)) } fn range(&self) -> Option { diff --git a/crates/red_knot_server/src/server/api/requests/diagnostic.rs b/crates/red_knot_server/src/server/api/requests/diagnostic.rs index c93467a911..968c05abe6 100644 --- a/crates/red_knot_server/src/server/api/requests/diagnostic.rs +++ b/crates/red_knot_server/src/server/api/requests/diagnostic.rs @@ -75,9 +75,9 @@ fn to_lsp_diagnostic( diagnostic: &dyn ruff_db::diagnostic::Diagnostic, encoding: crate::PositionEncoding, ) -> Diagnostic { - let range = if let Some(range) = diagnostic.range() { - let index = line_index(db.upcast(), diagnostic.file()); - let source = source_text(db.upcast(), diagnostic.file()); + let range = if let (Some(file), Some(range)) = (diagnostic.file(), diagnostic.range()) { + let index = line_index(db.upcast(), file); + let source = source_text(db.upcast(), file); range.to_range(&source, &index, encoding) } else { diff --git a/crates/red_knot_test/src/diagnostic.rs b/crates/red_knot_test/src/diagnostic.rs index 7666ff8b2c..bb5a099b50 100644 --- a/crates/red_knot_test/src/diagnostic.rs +++ b/crates/red_knot_test/src/diagnostic.rs @@ -198,8 +198,8 @@ mod tests { "dummy".into() } - fn file(&self) -> File { - self.file + fn file(&self) -> Option { + Some(self.file) } fn range(&self) -> Option { diff --git a/crates/red_knot_test/src/matcher.rs b/crates/red_knot_test/src/matcher.rs index 65d8409957..e7add5c50a 100644 --- a/crates/red_knot_test/src/matcher.rs +++ b/crates/red_knot_test/src/matcher.rs @@ -385,8 +385,8 @@ mod tests { self.message.into() } - fn file(&self) -> File { - self.file + fn file(&self) -> Option { + Some(self.file) } fn range(&self) -> Option { diff --git a/crates/ruff_db/src/diagnostic.rs b/crates/ruff_db/src/diagnostic.rs index 11f3a7db67..e48e17f127 100644 --- a/crates/ruff_db/src/diagnostic.rs +++ b/crates/ruff_db/src/diagnostic.rs @@ -152,8 +152,18 @@ pub trait Diagnostic: Send + Sync + std::fmt::Debug { fn message(&self) -> Cow; - fn file(&self) -> File; + /// The file this diagnostic is associated with. + /// + /// File can be `None` for diagnostics that don't originate from a file. + /// For example: + /// * A diagnostic indicating that a directory couldn't be read. + /// * A diagnostic related to a CLI argument + fn file(&self) -> Option; + /// The primary range of the diagnostic in `file`. + /// + /// The range can be `None` if the diagnostic doesn't have a file + /// or it applies to the entire file (e.g. the file should be executable but isn't). fn range(&self) -> Option; fn severity(&self) -> Severity; @@ -197,16 +207,15 @@ impl std::fmt::Display for DisplayDiagnostic<'_> { Severity::Fatal => f.write_str("fatal")?, } - write!( - f, - "[{rule}] {path}", - rule = self.diagnostic.id(), - path = self.diagnostic.file().path(self.db) - )?; + write!(f, "[{rule}]", rule = self.diagnostic.id())?; - if let Some(range) = self.diagnostic.range() { - let index = line_index(self.db, self.diagnostic.file()); - let source = source_text(self.db, self.diagnostic.file()); + if let Some(file) = self.diagnostic.file() { + write!(f, " {path}", path = file.path(self.db))?; + } + + if let (Some(file), Some(range)) = (self.diagnostic.file(), self.diagnostic.range()) { + let index = line_index(self.db, file); + let source = source_text(self.db, file); let start = index.source_location(range.start(), &source); @@ -229,7 +238,7 @@ where (**self).message() } - fn file(&self) -> File { + fn file(&self) -> Option { (**self).file() } @@ -254,7 +263,7 @@ where (**self).message() } - fn file(&self) -> File { + fn file(&self) -> Option { (**self).file() } @@ -276,7 +285,7 @@ impl Diagnostic for Box { (**self).message() } - fn file(&self) -> File { + fn file(&self) -> Option { (**self).file() } @@ -310,8 +319,8 @@ impl Diagnostic for ParseDiagnostic { self.error.error.to_string().into() } - fn file(&self) -> File { - self.file + fn file(&self) -> Option { + Some(self.file) } fn range(&self) -> Option {