-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Convert Message::SyntaxError to use Diagnostic internally
#17784
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5048108
2f01da5
3a04278
3c5065d
0e1a827
122e787
1559c29
9b50816
f717a6b
70f47bf
26c2009
97bf7f3
17753cb
007aa71
7f59117
056d369
f11b040
e9f2c36
6b6a45b
eb1e332
88ae57e
57581f2
53eb330
f22a1ad
a77d9fb
3f8ccd9
e76d2ec
60e18ad
1faa5e0
561f2ac
e29eb48
33b75e6
8614e56
3471834
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,15 @@ | ||
| use std::{fmt::Formatter, sync::Arc}; | ||
|
|
||
| use render::{FileResolver, Input}; | ||
| use ruff_source_file::{SourceCode, SourceFile}; | ||
| use thiserror::Error; | ||
|
|
||
| use ruff_annotate_snippets::Level as AnnotateLevel; | ||
| use ruff_text_size::{Ranged, TextRange}; | ||
|
|
||
| pub use self::render::DisplayDiagnostic; | ||
| use crate::files::File; | ||
| use crate::Db; | ||
| use crate::{files::File, Db}; | ||
|
|
||
| use self::render::FileResolver; | ||
| mod render; | ||
| mod stylesheet; | ||
|
|
||
|
|
@@ -115,10 +115,9 @@ impl Diagnostic { | |
| /// callers should prefer using this with `write!` instead of `writeln!`. | ||
| pub fn display<'a>( | ||
| &'a self, | ||
| db: &'a dyn Db, | ||
| resolver: &'a dyn FileResolver, | ||
| config: &'a DisplayDiagnosticConfig, | ||
| ) -> DisplayDiagnostic<'a> { | ||
| let resolver = FileResolver::new(db); | ||
| DisplayDiagnostic::new(resolver, config, self) | ||
| } | ||
|
|
||
|
|
@@ -233,6 +232,16 @@ impl Diagnostic { | |
| self.primary_annotation().map(|ann| ann.tags.as_slice()) | ||
| } | ||
|
|
||
| /// Returns the "primary" span of this diagnostic, panicking if it does not exist. | ||
| /// | ||
| /// This should typically only be used when working with diagnostics in Ruff, where diagnostics | ||
| /// are currently required to have a primary span. | ||
| /// | ||
| /// See [`Diagnostic::primary_span`] for more details. | ||
| pub fn expect_primary_span(&self) -> Span { | ||
| self.primary_span().expect("Expected a primary span") | ||
| } | ||
|
|
||
| /// Returns a key that can be used to sort two diagnostics into the canonical order | ||
| /// in which they should appear when rendered. | ||
| pub fn rendering_sort_key<'a>(&'a self, db: &'a dyn Db) -> impl Ord + 'a { | ||
|
|
@@ -267,11 +276,7 @@ impl Ord for RenderingSortKey<'_> { | |
| self.diagnostic.primary_span(), | ||
| other.diagnostic.primary_span(), | ||
| ) { | ||
| let order = span1 | ||
| .file() | ||
| .path(self.db) | ||
| .as_str() | ||
| .cmp(span2.file().path(self.db).as_str()); | ||
| let order = span1.file().path(&self.db).cmp(span2.file().path(&self.db)); | ||
| if order.is_ne() { | ||
| return order; | ||
| } | ||
|
|
@@ -643,6 +648,10 @@ impl DiagnosticId { | |
| DiagnosticId::UnknownRule => "unknown-rule", | ||
| }) | ||
| } | ||
|
|
||
| pub fn is_invalid_syntax(&self) -> bool { | ||
| matches!(self, Self::InvalidSyntax) | ||
| } | ||
| } | ||
|
|
||
| #[derive(Copy, Clone, Debug, Eq, PartialEq, Error)] | ||
|
|
@@ -668,21 +677,77 @@ impl std::fmt::Display for DiagnosticId { | |
| } | ||
| } | ||
|
|
||
| /// A unified file representation for both ruff and ty. | ||
| /// | ||
| /// Such a representation is needed for rendering [`Diagnostic`]s that can optionally contain | ||
| /// [`Annotation`]s with [`Span`]s that need to refer to the text of a file. However, ty and ruff | ||
| /// use very different file types: a `Copy`-able salsa-interned [`File`], and a heavier-weight | ||
| /// [`SourceFile`], respectively. | ||
| /// | ||
| /// This enum presents a unified interface to these two types for the sake of creating [`Span`]s and | ||
| /// emitting diagnostics from both ty and ruff. | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub enum UnifiedFile { | ||
| Ty(File), | ||
| Ruff(SourceFile), | ||
| } | ||
|
|
||
| impl UnifiedFile { | ||
| pub fn path<'a>(&'a self, resolver: &'a dyn FileResolver) -> &'a str { | ||
| match self { | ||
| UnifiedFile::Ty(file) => resolver.path(*file), | ||
| UnifiedFile::Ruff(file) => file.name(), | ||
| } | ||
| } | ||
|
||
|
|
||
| fn diagnostic_source(&self, resolver: &dyn FileResolver) -> DiagnosticSource { | ||
| match self { | ||
| UnifiedFile::Ty(file) => DiagnosticSource::Ty(resolver.input(*file)), | ||
| UnifiedFile::Ruff(file) => DiagnosticSource::Ruff(file.clone()), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// A unified wrapper for types that can be converted to a [`SourceCode`]. | ||
| /// | ||
| /// As with [`UnifiedFile`], ruff and ty use slightly different representations for source code. | ||
| /// [`DiagnosticSource`] wraps both of these and provides the single | ||
| /// [`DiagnosticSource::as_source_code`] method to produce a [`SourceCode`] with the appropriate | ||
| /// lifetimes. | ||
| /// | ||
| /// See [`UnifiedFile::diagnostic_source`] for a way to obtain a [`DiagnosticSource`] from a file | ||
| /// and [`FileResolver`]. | ||
| #[derive(Clone, Debug)] | ||
| enum DiagnosticSource { | ||
| Ty(Input), | ||
| Ruff(SourceFile), | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice docs. And I like this type. Much clearer. |
||
|
|
||
| impl DiagnosticSource { | ||
| /// Returns this input as a `SourceCode` for convenient querying. | ||
| fn as_source_code(&self) -> SourceCode { | ||
| match self { | ||
| DiagnosticSource::Ty(input) => SourceCode::new(input.text.as_str(), &input.line_index), | ||
| DiagnosticSource::Ruff(source) => SourceCode::new(source.source_text(), source.index()), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// A span represents the source of a diagnostic. | ||
| /// | ||
| /// It consists of a `File` and an optional range into that file. When the | ||
| /// range isn't present, it semantically implies that the diagnostic refers to | ||
| /// the entire file. For example, when the file should be executable but isn't. | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub struct Span { | ||
| file: File, | ||
| file: UnifiedFile, | ||
| range: Option<TextRange>, | ||
| } | ||
|
|
||
| impl Span { | ||
| /// Returns the `File` attached to this `Span`. | ||
| pub fn file(&self) -> File { | ||
| self.file | ||
| /// Returns the `UnifiedFile` attached to this `Span`. | ||
| pub fn file(&self) -> &UnifiedFile { | ||
| &self.file | ||
| } | ||
|
|
||
| /// Returns the range, if available, attached to this `Span`. | ||
|
|
@@ -703,10 +768,38 @@ impl Span { | |
| pub fn with_optional_range(self, range: Option<TextRange>) -> Span { | ||
| Span { range, ..self } | ||
| } | ||
|
|
||
| /// Returns the [`File`] attached to this [`Span`]. | ||
| /// | ||
| /// Panics if the file is a [`UnifiedFile::Ruff`] instead of a [`UnifiedFile::Ty`]. | ||
| pub fn expect_ty_file(&self) -> File { | ||
| match self.file { | ||
| UnifiedFile::Ty(file) => file, | ||
| UnifiedFile::Ruff(_) => panic!("Expected a ty `File`, found a ruff `SourceFile`"), | ||
| } | ||
| } | ||
|
|
||
| /// Returns the [`SourceFile`] attached to this [`Span`]. | ||
| /// | ||
| /// Panics if the file is a [`UnifiedFile::Ty`] instead of a [`UnifiedFile::Ruff`]. | ||
| pub fn expect_ruff_file(&self) -> &SourceFile { | ||
| match &self.file { | ||
| UnifiedFile::Ty(_) => panic!("Expected a ruff `SourceFile`, found a ty `File`"), | ||
| UnifiedFile::Ruff(file) => file, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<File> for Span { | ||
| fn from(file: File) -> Span { | ||
| let file = UnifiedFile::Ty(file); | ||
| Span { file, range: None } | ||
| } | ||
| } | ||
|
|
||
| impl From<SourceFile> for Span { | ||
| fn from(file: SourceFile) -> Self { | ||
| let file = UnifiedFile::Ruff(file); | ||
| Span { file, range: None } | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add docs to this type? This might be a nice place to put some prose about why this exists and the high level strategy for how we're trying to share the diagnostics system between both ty and ruff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, much better!