mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-22 08:12:17 +00:00
Move fix suggestion to subdiagnostic (#19464)
Summary -- This PR tweaks Ruff's internal usage of the new diagnostic model to more closely match the intended use, as I understand it. Specifically, it moves the fix/help suggestion from the primary annotation's message to a subdiagnostic. In turn, it adds the secondary/noqa code as the new primary annotation message. As shown in the new `ruff_db` tests, this more closely mirrors Ruff's current diagnostic output. I also added `Severity::Help` to render the fix suggestion with a `help:` prefix instead of `info:`. These changes don't have any external impact now but should help a bit with #19415. Test Plan -- New full output format tests in `ruff_db` Rendered Diagnostics -- Full diagnostic output from `annotate-snippets` in this PR: ``` error[unused-import]: `os` imported but unused --> fib.py:1:8 | 1 | import os | ^^ | help: Remove unused import: `os` ``` Current Ruff output for the same code: ``` fib.py:1:8: F401 [*] `os` imported but unused | 1 | import os | ^^ F401 | = help: Remove unused import: `os` ``` Proposed final output after #19415: ``` F401 [*] `os` imported but unused --> fib.py:1:8 | 1 | import os | ^^ | help: Remove unused import: `os` ``` These are slightly updated from https://github.com/astral-sh/ruff/pull/19464#issuecomment-3097377634 below to remove the extra noqa codes in the primary annotation messages for the first and third cases.
This commit is contained in:
parent
c82fa94e0a
commit
fd335eb8b7
20 changed files with 235 additions and 106 deletions
|
@ -454,7 +454,7 @@ impl LintCacheData {
|
||||||
CacheMessage {
|
CacheMessage {
|
||||||
rule,
|
rule,
|
||||||
body: msg.body().to_string(),
|
body: msg.body().to_string(),
|
||||||
suggestion: msg.suggestion().map(ToString::to_string),
|
suggestion: msg.first_help_text().map(ToString::to_string),
|
||||||
range: msg.expect_range(),
|
range: msg.expect_range(),
|
||||||
parent: msg.parent(),
|
parent: msg.parent(),
|
||||||
fix: msg.fix().cloned(),
|
fix: msg.fix().cloned(),
|
||||||
|
|
|
@ -122,7 +122,14 @@ impl Diagnostic {
|
||||||
/// directly. If callers want or need to avoid cloning the diagnostic
|
/// directly. If callers want or need to avoid cloning the diagnostic
|
||||||
/// message, then they can also pass a `DiagnosticMessage` directly.
|
/// message, then they can also pass a `DiagnosticMessage` directly.
|
||||||
pub fn info<'a>(&mut self, message: impl IntoDiagnosticMessage + 'a) {
|
pub fn info<'a>(&mut self, message: impl IntoDiagnosticMessage + 'a) {
|
||||||
self.sub(SubDiagnostic::new(Severity::Info, message));
|
self.sub(SubDiagnostic::new(SubDiagnosticSeverity::Info, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a "help" sub-diagnostic with the given message.
|
||||||
|
///
|
||||||
|
/// See the closely related [`Diagnostic::info`] method for more details.
|
||||||
|
pub fn help<'a>(&mut self, message: impl IntoDiagnosticMessage + 'a) {
|
||||||
|
self.sub(SubDiagnostic::new(SubDiagnosticSeverity::Help, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a "sub" diagnostic to this diagnostic.
|
/// Adds a "sub" diagnostic to this diagnostic.
|
||||||
|
@ -377,9 +384,15 @@ impl Diagnostic {
|
||||||
self.primary_message()
|
self.primary_message()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the fix suggestion for the violation.
|
/// Returns the message of the first sub-diagnostic with a `Help` severity.
|
||||||
pub fn suggestion(&self) -> Option<&str> {
|
///
|
||||||
self.primary_annotation()?.get_message()
|
/// Note that this is used as the fix title/suggestion for some of Ruff's output formats, but in
|
||||||
|
/// general this is not the guaranteed meaning of such a message.
|
||||||
|
pub fn first_help_text(&self) -> Option<&str> {
|
||||||
|
self.sub_diagnostics()
|
||||||
|
.iter()
|
||||||
|
.find(|sub| matches!(sub.inner.severity, SubDiagnosticSeverity::Help))
|
||||||
|
.map(|sub| sub.inner.message.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the URL for the rule documentation, if it exists.
|
/// Returns the URL for the rule documentation, if it exists.
|
||||||
|
@ -565,7 +578,10 @@ impl SubDiagnostic {
|
||||||
/// Callers can pass anything that implements `std::fmt::Display`
|
/// Callers can pass anything that implements `std::fmt::Display`
|
||||||
/// directly. If callers want or need to avoid cloning the diagnostic
|
/// directly. If callers want or need to avoid cloning the diagnostic
|
||||||
/// message, then they can also pass a `DiagnosticMessage` directly.
|
/// message, then they can also pass a `DiagnosticMessage` directly.
|
||||||
pub fn new<'a>(severity: Severity, message: impl IntoDiagnosticMessage + 'a) -> SubDiagnostic {
|
pub fn new<'a>(
|
||||||
|
severity: SubDiagnosticSeverity,
|
||||||
|
message: impl IntoDiagnosticMessage + 'a,
|
||||||
|
) -> SubDiagnostic {
|
||||||
let inner = Box::new(SubDiagnosticInner {
|
let inner = Box::new(SubDiagnosticInner {
|
||||||
severity,
|
severity,
|
||||||
message: message.into_diagnostic_message(),
|
message: message.into_diagnostic_message(),
|
||||||
|
@ -643,7 +659,7 @@ impl SubDiagnostic {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)]
|
#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)]
|
||||||
struct SubDiagnosticInner {
|
struct SubDiagnosticInner {
|
||||||
severity: Severity,
|
severity: SubDiagnosticSeverity,
|
||||||
message: DiagnosticMessage,
|
message: DiagnosticMessage,
|
||||||
annotations: Vec<Annotation>,
|
annotations: Vec<Annotation>,
|
||||||
}
|
}
|
||||||
|
@ -1170,6 +1186,30 @@ impl Severity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Like [`Severity`] but exclusively for sub-diagnostics.
|
||||||
|
///
|
||||||
|
/// This supports an additional `Help` severity that may not be needed in main diagnostics.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, get_size2::GetSize)]
|
||||||
|
pub enum SubDiagnosticSeverity {
|
||||||
|
Help,
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
Fatal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubDiagnosticSeverity {
|
||||||
|
fn to_annotate(self) -> AnnotateLevel {
|
||||||
|
match self {
|
||||||
|
SubDiagnosticSeverity::Help => AnnotateLevel::Help,
|
||||||
|
SubDiagnosticSeverity::Info => AnnotateLevel::Info,
|
||||||
|
SubDiagnosticSeverity::Warning => AnnotateLevel::Warning,
|
||||||
|
SubDiagnosticSeverity::Error => AnnotateLevel::Error,
|
||||||
|
SubDiagnosticSeverity::Fatal => AnnotateLevel::Error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Configuration for rendering diagnostics.
|
/// Configuration for rendering diagnostics.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DisplayDiagnosticConfig {
|
pub struct DisplayDiagnosticConfig {
|
||||||
|
|
|
@ -26,6 +26,7 @@ use azure::AzureRenderer;
|
||||||
use pylint::PylintRenderer;
|
use pylint::PylintRenderer;
|
||||||
|
|
||||||
mod azure;
|
mod azure;
|
||||||
|
mod full;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
mod json;
|
mod json;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
@ -256,7 +257,7 @@ impl<'a> Resolved<'a> {
|
||||||
/// both.)
|
/// both.)
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ResolvedDiagnostic<'a> {
|
struct ResolvedDiagnostic<'a> {
|
||||||
severity: Severity,
|
level: AnnotateLevel,
|
||||||
id: Option<String>,
|
id: Option<String>,
|
||||||
message: String,
|
message: String,
|
||||||
annotations: Vec<ResolvedAnnotation<'a>>,
|
annotations: Vec<ResolvedAnnotation<'a>>,
|
||||||
|
@ -281,7 +282,7 @@ impl<'a> ResolvedDiagnostic<'a> {
|
||||||
let id = Some(diag.inner.id.to_string());
|
let id = Some(diag.inner.id.to_string());
|
||||||
let message = diag.inner.message.as_str().to_string();
|
let message = diag.inner.message.as_str().to_string();
|
||||||
ResolvedDiagnostic {
|
ResolvedDiagnostic {
|
||||||
severity: diag.inner.severity,
|
level: diag.inner.severity.to_annotate(),
|
||||||
id,
|
id,
|
||||||
message,
|
message,
|
||||||
annotations,
|
annotations,
|
||||||
|
@ -304,7 +305,7 @@ impl<'a> ResolvedDiagnostic<'a> {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
ResolvedDiagnostic {
|
ResolvedDiagnostic {
|
||||||
severity: diag.inner.severity,
|
level: diag.inner.severity.to_annotate(),
|
||||||
id: None,
|
id: None,
|
||||||
message: diag.inner.message.as_str().to_string(),
|
message: diag.inner.message.as_str().to_string(),
|
||||||
annotations,
|
annotations,
|
||||||
|
@ -371,7 +372,7 @@ impl<'a> ResolvedDiagnostic<'a> {
|
||||||
snippets_by_input
|
snippets_by_input
|
||||||
.sort_by(|snips1, snips2| snips1.has_primary.cmp(&snips2.has_primary).reverse());
|
.sort_by(|snips1, snips2| snips1.has_primary.cmp(&snips2.has_primary).reverse());
|
||||||
RenderableDiagnostic {
|
RenderableDiagnostic {
|
||||||
severity: self.severity,
|
level: self.level,
|
||||||
id: self.id.as_deref(),
|
id: self.id.as_deref(),
|
||||||
message: &self.message,
|
message: &self.message,
|
||||||
snippets_by_input,
|
snippets_by_input,
|
||||||
|
@ -459,7 +460,7 @@ struct Renderable<'r> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct RenderableDiagnostic<'r> {
|
struct RenderableDiagnostic<'r> {
|
||||||
/// The severity of the diagnostic.
|
/// The severity of the diagnostic.
|
||||||
severity: Severity,
|
level: AnnotateLevel,
|
||||||
/// The ID of the diagnostic. The ID can usually be used on the CLI or in a
|
/// The ID of the diagnostic. The ID can usually be used on the CLI or in a
|
||||||
/// config file to change the severity of a lint.
|
/// config file to change the severity of a lint.
|
||||||
///
|
///
|
||||||
|
@ -478,7 +479,6 @@ struct RenderableDiagnostic<'r> {
|
||||||
impl RenderableDiagnostic<'_> {
|
impl RenderableDiagnostic<'_> {
|
||||||
/// Convert this to an "annotate" snippet.
|
/// Convert this to an "annotate" snippet.
|
||||||
fn to_annotate(&self) -> AnnotateMessage<'_> {
|
fn to_annotate(&self) -> AnnotateMessage<'_> {
|
||||||
let level = self.severity.to_annotate();
|
|
||||||
let snippets = self.snippets_by_input.iter().flat_map(|snippets| {
|
let snippets = self.snippets_by_input.iter().flat_map(|snippets| {
|
||||||
let path = snippets.path;
|
let path = snippets.path;
|
||||||
snippets
|
snippets
|
||||||
|
@ -486,7 +486,7 @@ impl RenderableDiagnostic<'_> {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|snippet| snippet.to_annotate(path))
|
.map(|snippet| snippet.to_annotate(path))
|
||||||
});
|
});
|
||||||
let mut message = level.title(self.message);
|
let mut message = self.level.title(self.message);
|
||||||
if let Some(id) = self.id {
|
if let Some(id) = self.id {
|
||||||
message = message.id(id);
|
message = message.id(id);
|
||||||
}
|
}
|
||||||
|
@ -864,7 +864,10 @@ mod tests {
|
||||||
|
|
||||||
use ruff_diagnostics::{Edit, Fix};
|
use ruff_diagnostics::{Edit, Fix};
|
||||||
|
|
||||||
use crate::diagnostic::{Annotation, DiagnosticId, SecondaryCode, Severity, Span};
|
use crate::diagnostic::{
|
||||||
|
Annotation, DiagnosticId, IntoDiagnosticMessage, SecondaryCode, Severity, Span,
|
||||||
|
SubDiagnosticSeverity,
|
||||||
|
};
|
||||||
use crate::files::system_path_to_file;
|
use crate::files::system_path_to_file;
|
||||||
use crate::system::{DbWithWritableSystem, SystemPath};
|
use crate::system::{DbWithWritableSystem, SystemPath};
|
||||||
use crate::tests::TestDb;
|
use crate::tests::TestDb;
|
||||||
|
@ -1548,7 +1551,7 @@ watermelon
|
||||||
|
|
||||||
let mut diag = env.err().primary("animals", "3", "3", "").build();
|
let mut diag = env.err().primary("animals", "3", "3", "").build();
|
||||||
diag.sub(
|
diag.sub(
|
||||||
env.sub_builder(Severity::Info, "this is a helpful note")
|
env.sub_builder(SubDiagnosticSeverity::Info, "this is a helpful note")
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
insta::assert_snapshot!(
|
insta::assert_snapshot!(
|
||||||
|
@ -1577,15 +1580,15 @@ watermelon
|
||||||
|
|
||||||
let mut diag = env.err().primary("animals", "3", "3", "").build();
|
let mut diag = env.err().primary("animals", "3", "3", "").build();
|
||||||
diag.sub(
|
diag.sub(
|
||||||
env.sub_builder(Severity::Info, "this is a helpful note")
|
env.sub_builder(SubDiagnosticSeverity::Info, "this is a helpful note")
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
diag.sub(
|
diag.sub(
|
||||||
env.sub_builder(Severity::Info, "another helpful note")
|
env.sub_builder(SubDiagnosticSeverity::Info, "another helpful note")
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
diag.sub(
|
diag.sub(
|
||||||
env.sub_builder(Severity::Info, "and another helpful note")
|
env.sub_builder(SubDiagnosticSeverity::Info, "and another helpful note")
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
insta::assert_snapshot!(
|
insta::assert_snapshot!(
|
||||||
|
@ -2370,7 +2373,7 @@ watermelon
|
||||||
/// sub-diagnostic with "error" severity and canned values for
|
/// sub-diagnostic with "error" severity and canned values for
|
||||||
/// its identifier and message.
|
/// its identifier and message.
|
||||||
fn sub_warn(&mut self) -> SubDiagnosticBuilder<'_> {
|
fn sub_warn(&mut self) -> SubDiagnosticBuilder<'_> {
|
||||||
self.sub_builder(Severity::Warning, "sub-diagnostic message")
|
self.sub_builder(SubDiagnosticSeverity::Warning, "sub-diagnostic message")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a builder for tersely constructing diagnostics.
|
/// Returns a builder for tersely constructing diagnostics.
|
||||||
|
@ -2391,7 +2394,11 @@ watermelon
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a builder for tersely constructing sub-diagnostics.
|
/// Returns a builder for tersely constructing sub-diagnostics.
|
||||||
fn sub_builder(&mut self, severity: Severity, message: &str) -> SubDiagnosticBuilder<'_> {
|
fn sub_builder(
|
||||||
|
&mut self,
|
||||||
|
severity: SubDiagnosticSeverity,
|
||||||
|
message: &str,
|
||||||
|
) -> SubDiagnosticBuilder<'_> {
|
||||||
let subdiag = SubDiagnostic::new(severity, message);
|
let subdiag = SubDiagnostic::new(severity, message);
|
||||||
SubDiagnosticBuilder { env: self, subdiag }
|
SubDiagnosticBuilder { env: self, subdiag }
|
||||||
}
|
}
|
||||||
|
@ -2494,6 +2501,12 @@ watermelon
|
||||||
self.diag.set_noqa_offset(noqa_offset);
|
self.diag.set_noqa_offset(noqa_offset);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a "help" sub-diagnostic with the given message.
|
||||||
|
fn help(mut self, message: impl IntoDiagnosticMessage) -> DiagnosticBuilder<'e> {
|
||||||
|
self.diag.help(message);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper builder for tersely populating a `SubDiagnostic`.
|
/// A helper builder for tersely populating a `SubDiagnostic`.
|
||||||
|
@ -2600,7 +2613,8 @@ def fibonacci(n):
|
||||||
|
|
||||||
let diagnostics = vec![
|
let diagnostics = vec![
|
||||||
env.builder("unused-import", Severity::Error, "`os` imported but unused")
|
env.builder("unused-import", Severity::Error, "`os` imported but unused")
|
||||||
.primary("fib.py", "1:7", "1:9", "Remove unused import: `os`")
|
.primary("fib.py", "1:7", "1:9", "")
|
||||||
|
.help("Remove unused import: `os`")
|
||||||
.secondary_code("F401")
|
.secondary_code("F401")
|
||||||
.fix(Fix::unsafe_edit(Edit::range_deletion(TextRange::new(
|
.fix(Fix::unsafe_edit(Edit::range_deletion(TextRange::new(
|
||||||
TextSize::from(0),
|
TextSize::from(0),
|
||||||
|
@ -2613,12 +2627,8 @@ def fibonacci(n):
|
||||||
Severity::Error,
|
Severity::Error,
|
||||||
"Local variable `x` is assigned to but never used",
|
"Local variable `x` is assigned to but never used",
|
||||||
)
|
)
|
||||||
.primary(
|
.primary("fib.py", "6:4", "6:5", "")
|
||||||
"fib.py",
|
.help("Remove assignment to unused variable `x`")
|
||||||
"6:4",
|
|
||||||
"6:5",
|
|
||||||
"Remove assignment to unused variable `x`",
|
|
||||||
)
|
|
||||||
.secondary_code("F841")
|
.secondary_code("F841")
|
||||||
.fix(Fix::unsafe_edit(Edit::deletion(
|
.fix(Fix::unsafe_edit(Edit::deletion(
|
||||||
TextSize::from(94),
|
TextSize::from(94),
|
||||||
|
@ -2720,7 +2730,8 @@ if call(foo
|
||||||
|
|
||||||
let diagnostics = vec![
|
let diagnostics = vec![
|
||||||
env.builder("unused-import", Severity::Error, "`os` imported but unused")
|
env.builder("unused-import", Severity::Error, "`os` imported but unused")
|
||||||
.primary("notebook.ipynb", "2:7", "2:9", "Remove unused import: `os`")
|
.primary("notebook.ipynb", "2:7", "2:9", "")
|
||||||
|
.help("Remove unused import: `os`")
|
||||||
.secondary_code("F401")
|
.secondary_code("F401")
|
||||||
.fix(Fix::safe_edit(Edit::range_deletion(TextRange::new(
|
.fix(Fix::safe_edit(Edit::range_deletion(TextRange::new(
|
||||||
TextSize::from(9),
|
TextSize::from(9),
|
||||||
|
@ -2733,12 +2744,8 @@ if call(foo
|
||||||
Severity::Error,
|
Severity::Error,
|
||||||
"`math` imported but unused",
|
"`math` imported but unused",
|
||||||
)
|
)
|
||||||
.primary(
|
.primary("notebook.ipynb", "4:7", "4:11", "")
|
||||||
"notebook.ipynb",
|
.help("Remove unused import: `math`")
|
||||||
"4:7",
|
|
||||||
"4:11",
|
|
||||||
"Remove unused import: `math`",
|
|
||||||
)
|
|
||||||
.secondary_code("F401")
|
.secondary_code("F401")
|
||||||
.fix(Fix::safe_edit(Edit::range_deletion(TextRange::new(
|
.fix(Fix::safe_edit(Edit::range_deletion(TextRange::new(
|
||||||
TextSize::from(28),
|
TextSize::from(28),
|
||||||
|
@ -2751,12 +2758,8 @@ if call(foo
|
||||||
Severity::Error,
|
Severity::Error,
|
||||||
"Local variable `x` is assigned to but never used",
|
"Local variable `x` is assigned to but never used",
|
||||||
)
|
)
|
||||||
.primary(
|
.primary("notebook.ipynb", "10:4", "10:5", "")
|
||||||
"notebook.ipynb",
|
.help("Remove assignment to unused variable `x`")
|
||||||
"10:4",
|
|
||||||
"10:5",
|
|
||||||
"Remove assignment to unused variable `x`",
|
|
||||||
)
|
|
||||||
.secondary_code("F841")
|
.secondary_code("F841")
|
||||||
.fix(Fix::unsafe_edit(Edit::range_deletion(TextRange::new(
|
.fix(Fix::unsafe_edit(Edit::range_deletion(TextRange::new(
|
||||||
TextSize::from(94),
|
TextSize::from(94),
|
||||||
|
|
66
crates/ruff_db/src/diagnostic/render/full.rs
Normal file
66
crates/ruff_db/src/diagnostic/render/full.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::diagnostic::{
|
||||||
|
DiagnosticFormat,
|
||||||
|
render::tests::{create_diagnostics, create_syntax_error_diagnostics},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn output() {
|
||||||
|
let (env, diagnostics) = create_diagnostics(DiagnosticFormat::Full);
|
||||||
|
insta::assert_snapshot!(env.render_diagnostics(&diagnostics), @r#"
|
||||||
|
error[unused-import]: `os` imported but unused
|
||||||
|
--> fib.py:1:8
|
||||||
|
|
|
||||||
|
1 | import os
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
help: Remove unused import: `os`
|
||||||
|
|
||||||
|
error[unused-variable]: Local variable `x` is assigned to but never used
|
||||||
|
--> fib.py:6:5
|
||||||
|
|
|
||||||
|
4 | def fibonacci(n):
|
||||||
|
5 | """Compute the nth number in the Fibonacci sequence."""
|
||||||
|
6 | x = 1
|
||||||
|
| ^
|
||||||
|
7 | if n == 0:
|
||||||
|
8 | return 0
|
||||||
|
|
|
||||||
|
help: Remove assignment to unused variable `x`
|
||||||
|
|
||||||
|
error[undefined-name]: Undefined name `a`
|
||||||
|
--> undef.py:1:4
|
||||||
|
|
|
||||||
|
1 | if a == 1: pass
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn syntax_errors() {
|
||||||
|
let (env, diagnostics) = create_syntax_error_diagnostics(DiagnosticFormat::Full);
|
||||||
|
insta::assert_snapshot!(env.render_diagnostics(&diagnostics), @r"
|
||||||
|
error[invalid-syntax]: SyntaxError: Expected one or more symbol names after import
|
||||||
|
--> syntax_errors.py:1:15
|
||||||
|
|
|
||||||
|
1 | from os import
|
||||||
|
| ^
|
||||||
|
2 |
|
||||||
|
3 | if call(foo
|
||||||
|
|
|
||||||
|
|
||||||
|
error[invalid-syntax]: SyntaxError: Expected ')', found newline
|
||||||
|
--> syntax_errors.py:3:12
|
||||||
|
|
|
||||||
|
1 | from os import
|
||||||
|
2 |
|
||||||
|
3 | if call(foo
|
||||||
|
| ^
|
||||||
|
4 | def bar():
|
||||||
|
5 | pass
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
}
|
|
@ -87,7 +87,7 @@ pub(super) fn diagnostic_to_json<'a>(
|
||||||
|
|
||||||
let fix = diagnostic.fix().map(|fix| JsonFix {
|
let fix = diagnostic.fix().map(|fix| JsonFix {
|
||||||
applicability: fix.applicability(),
|
applicability: fix.applicability(),
|
||||||
message: diagnostic.suggestion(),
|
message: diagnostic.first_help_text(),
|
||||||
edits: ExpandedEdits {
|
edits: ExpandedEdits {
|
||||||
edits: fix.edits(),
|
edits: fix.edits(),
|
||||||
notebook_index,
|
notebook_index,
|
||||||
|
|
|
@ -75,12 +75,13 @@ where
|
||||||
);
|
);
|
||||||
|
|
||||||
let span = Span::from(file).with_range(range);
|
let span = Span::from(file).with_range(range);
|
||||||
let mut annotation = Annotation::primary(span);
|
let annotation = Annotation::primary(span);
|
||||||
if let Some(suggestion) = suggestion {
|
|
||||||
annotation = annotation.message(suggestion);
|
|
||||||
}
|
|
||||||
diagnostic.annotate(annotation);
|
diagnostic.annotate(annotation);
|
||||||
|
|
||||||
|
if let Some(suggestion) = suggestion {
|
||||||
|
diagnostic.help(suggestion);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(fix) = fix {
|
if let Some(fix) = fix {
|
||||||
diagnostic.set_fix(fix);
|
diagnostic.set_fix(fix);
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,7 +186,7 @@ pub(super) struct MessageCodeFrame<'a> {
|
||||||
|
|
||||||
impl Display for MessageCodeFrame<'_> {
|
impl Display for MessageCodeFrame<'_> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
let suggestion = self.message.suggestion();
|
let suggestion = self.message.first_help_text();
|
||||||
let footers = if let Some(suggestion) = suggestion {
|
let footers = if let Some(suggestion) = suggestion {
|
||||||
vec![Level::Help.title(suggestion)]
|
vec![Level::Help.title(suggestion)]
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -272,7 +272,7 @@ Either ensure you always emit a fix or change `Violation::FIX_AVAILABILITY` to e
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
!(fixable && diagnostic.suggestion().is_none()),
|
!(fixable && diagnostic.first_help_text().is_none()),
|
||||||
"Diagnostic emitted by {rule:?} is fixable but \
|
"Diagnostic emitted by {rule:?} is fixable but \
|
||||||
`Violation::fix_title` returns `None`"
|
`Violation::fix_title` returns `None`"
|
||||||
);
|
);
|
||||||
|
|
|
@ -238,7 +238,7 @@ fn to_lsp_diagnostic(
|
||||||
let name = diagnostic.name();
|
let name = diagnostic.name();
|
||||||
let body = diagnostic.body().to_string();
|
let body = diagnostic.body().to_string();
|
||||||
let fix = diagnostic.fix();
|
let fix = diagnostic.fix();
|
||||||
let suggestion = diagnostic.suggestion();
|
let suggestion = diagnostic.first_help_text();
|
||||||
let code = diagnostic.secondary_code();
|
let code = diagnostic.secondary_code();
|
||||||
|
|
||||||
let fix = fix.and_then(|fix| fix.applies(Applicability::Unsafe).then_some(fix));
|
let fix = fix.and_then(|fix| fix.applies(Applicability::Unsafe).then_some(fix));
|
||||||
|
|
|
@ -234,7 +234,7 @@ impl Workspace {
|
||||||
start_location: source_code.line_column(msg.expect_range().start()).into(),
|
start_location: source_code.line_column(msg.expect_range().start()).into(),
|
||||||
end_location: source_code.line_column(msg.expect_range().end()).into(),
|
end_location: source_code.line_column(msg.expect_range().end()).into(),
|
||||||
fix: msg.fix().map(|fix| ExpandedFix {
|
fix: msg.fix().map(|fix| ExpandedFix {
|
||||||
message: msg.suggestion().map(ToString::to_string),
|
message: msg.first_help_text().map(ToString::to_string),
|
||||||
edits: fix
|
edits: fix
|
||||||
.edits()
|
.edits()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -32,6 +32,7 @@ mod tests {
|
||||||
use insta::assert_snapshot;
|
use insta::assert_snapshot;
|
||||||
use ruff_db::diagnostic::{
|
use ruff_db::diagnostic::{
|
||||||
Annotation, Diagnostic, DiagnosticId, LintName, Severity, Span, SubDiagnostic,
|
Annotation, Diagnostic, DiagnosticId, LintName, Severity, Span, SubDiagnostic,
|
||||||
|
SubDiagnosticSeverity,
|
||||||
};
|
};
|
||||||
use ruff_db::files::FileRange;
|
use ruff_db::files::FileRange;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
@ -1349,7 +1350,7 @@ class MyClass:
|
||||||
|
|
||||||
impl IntoDiagnostic for GotoDeclarationDiagnostic {
|
impl IntoDiagnostic for GotoDeclarationDiagnostic {
|
||||||
fn into_diagnostic(self) -> Diagnostic {
|
fn into_diagnostic(self) -> Diagnostic {
|
||||||
let mut source = SubDiagnostic::new(Severity::Info, "Source");
|
let mut source = SubDiagnostic::new(SubDiagnosticSeverity::Info, "Source");
|
||||||
source.annotate(Annotation::primary(
|
source.annotate(Annotation::primary(
|
||||||
Span::from(self.source.file()).with_range(self.source.range()),
|
Span::from(self.source.file()).with_range(self.source.range()),
|
||||||
));
|
));
|
||||||
|
|
|
@ -37,6 +37,7 @@ mod test {
|
||||||
use insta::assert_snapshot;
|
use insta::assert_snapshot;
|
||||||
use ruff_db::diagnostic::{
|
use ruff_db::diagnostic::{
|
||||||
Annotation, Diagnostic, DiagnosticId, LintName, Severity, Span, SubDiagnostic,
|
Annotation, Diagnostic, DiagnosticId, LintName, Severity, Span, SubDiagnostic,
|
||||||
|
SubDiagnosticSeverity,
|
||||||
};
|
};
|
||||||
use ruff_db::files::FileRange;
|
use ruff_db::files::FileRange;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
@ -575,7 +576,7 @@ class MyClass: ...
|
||||||
|
|
||||||
impl IntoDiagnostic for GotoDefinitionDiagnostic {
|
impl IntoDiagnostic for GotoDefinitionDiagnostic {
|
||||||
fn into_diagnostic(self) -> Diagnostic {
|
fn into_diagnostic(self) -> Diagnostic {
|
||||||
let mut source = SubDiagnostic::new(Severity::Info, "Source");
|
let mut source = SubDiagnostic::new(SubDiagnosticSeverity::Info, "Source");
|
||||||
source.annotate(Annotation::primary(
|
source.annotate(Annotation::primary(
|
||||||
Span::from(self.source.file()).with_range(self.source.range()),
|
Span::from(self.source.file()).with_range(self.source.range()),
|
||||||
));
|
));
|
||||||
|
|
|
@ -33,6 +33,7 @@ mod tests {
|
||||||
use insta::assert_snapshot;
|
use insta::assert_snapshot;
|
||||||
use ruff_db::diagnostic::{
|
use ruff_db::diagnostic::{
|
||||||
Annotation, Diagnostic, DiagnosticId, LintName, Severity, Span, SubDiagnostic,
|
Annotation, Diagnostic, DiagnosticId, LintName, Severity, Span, SubDiagnostic,
|
||||||
|
SubDiagnosticSeverity,
|
||||||
};
|
};
|
||||||
use ruff_db::files::FileRange;
|
use ruff_db::files::FileRange;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
@ -640,7 +641,7 @@ f(**kwargs<CURSOR>)
|
||||||
|
|
||||||
impl IntoDiagnostic for GotoTypeDefinitionDiagnostic {
|
impl IntoDiagnostic for GotoTypeDefinitionDiagnostic {
|
||||||
fn into_diagnostic(self) -> Diagnostic {
|
fn into_diagnostic(self) -> Diagnostic {
|
||||||
let mut source = SubDiagnostic::new(Severity::Info, "Source");
|
let mut source = SubDiagnostic::new(SubDiagnosticSeverity::Info, "Source");
|
||||||
source.annotate(Annotation::primary(
|
source.annotate(Annotation::primary(
|
||||||
Span::from(self.source.file()).with_range(self.source.range()),
|
Span::from(self.source.file()).with_range(self.source.range()),
|
||||||
));
|
));
|
||||||
|
|
|
@ -5,7 +5,9 @@ pub use db::{ChangeResult, CheckMode, Db, ProjectDatabase, SalsaMemoryDump};
|
||||||
use files::{Index, Indexed, IndexedFiles};
|
use files::{Index, Indexed, IndexedFiles};
|
||||||
use metadata::settings::Settings;
|
use metadata::settings::Settings;
|
||||||
pub use metadata::{ProjectMetadata, ProjectMetadataError};
|
pub use metadata::{ProjectMetadata, ProjectMetadataError};
|
||||||
use ruff_db::diagnostic::{Annotation, Diagnostic, DiagnosticId, Severity, Span, SubDiagnostic};
|
use ruff_db::diagnostic::{
|
||||||
|
Annotation, Diagnostic, DiagnosticId, Severity, Span, SubDiagnostic, SubDiagnosticSeverity,
|
||||||
|
};
|
||||||
use ruff_db::files::{File, FileRootKind};
|
use ruff_db::files::{File, FileRootKind};
|
||||||
use ruff_db::parsed::parsed_module;
|
use ruff_db::parsed::parsed_module;
|
||||||
use ruff_db::source::{SourceTextError, source_text};
|
use ruff_db::source::{SourceTextError, source_text};
|
||||||
|
@ -674,14 +676,17 @@ where
|
||||||
|
|
||||||
let mut diagnostic = Diagnostic::new(DiagnosticId::Panic, Severity::Fatal, message);
|
let mut diagnostic = Diagnostic::new(DiagnosticId::Panic, Severity::Fatal, message);
|
||||||
diagnostic.sub(SubDiagnostic::new(
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"This indicates a bug in ty.",
|
"This indicates a bug in ty.",
|
||||||
));
|
));
|
||||||
|
|
||||||
let report_message = "If you could open an issue at https://github.com/astral-sh/ty/issues/new?title=%5Bpanic%5D, we'd be very appreciative!";
|
let report_message = "If you could open an issue at https://github.com/astral-sh/ty/issues/new?title=%5Bpanic%5D, we'd be very appreciative!";
|
||||||
diagnostic.sub(SubDiagnostic::new(Severity::Info, report_message));
|
|
||||||
diagnostic.sub(SubDiagnostic::new(
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
|
report_message,
|
||||||
|
));
|
||||||
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
|
SubDiagnosticSeverity::Info,
|
||||||
format!(
|
format!(
|
||||||
"Platform: {os} {arch}",
|
"Platform: {os} {arch}",
|
||||||
os = std::env::consts::OS,
|
os = std::env::consts::OS,
|
||||||
|
@ -690,13 +695,13 @@ where
|
||||||
));
|
));
|
||||||
if let Some(version) = ruff_db::program_version() {
|
if let Some(version) = ruff_db::program_version() {
|
||||||
diagnostic.sub(SubDiagnostic::new(
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format!("Version: {version}"),
|
format!("Version: {version}"),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostic.sub(SubDiagnostic::new(
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format!(
|
format!(
|
||||||
"Args: {args:?}",
|
"Args: {args:?}",
|
||||||
args = std::env::args().collect::<Vec<_>>()
|
args = std::env::args().collect::<Vec<_>>()
|
||||||
|
@ -707,13 +712,13 @@ where
|
||||||
match backtrace.status() {
|
match backtrace.status() {
|
||||||
BacktraceStatus::Disabled => {
|
BacktraceStatus::Disabled => {
|
||||||
diagnostic.sub(SubDiagnostic::new(
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"run with `RUST_BACKTRACE=1` environment variable to show the full backtrace information",
|
"run with `RUST_BACKTRACE=1` environment variable to show the full backtrace information",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
BacktraceStatus::Captured => {
|
BacktraceStatus::Captured => {
|
||||||
diagnostic.sub(SubDiagnostic::new(
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format!("Backtrace:\n{backtrace}"),
|
format!("Backtrace:\n{backtrace}"),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -723,7 +728,10 @@ where
|
||||||
|
|
||||||
if let Some(backtrace) = error.salsa_backtrace {
|
if let Some(backtrace) = error.salsa_backtrace {
|
||||||
salsa::attach(db, || {
|
salsa::attach(db, || {
|
||||||
diagnostic.sub(SubDiagnostic::new(Severity::Info, backtrace.to_string()));
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
|
SubDiagnosticSeverity::Info,
|
||||||
|
backtrace.to_string(),
|
||||||
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use ordermap::OrderMap;
|
||||||
use ruff_db::RustDoc;
|
use ruff_db::RustDoc;
|
||||||
use ruff_db::diagnostic::{
|
use ruff_db::diagnostic::{
|
||||||
Annotation, Diagnostic, DiagnosticFormat, DiagnosticId, DisplayDiagnosticConfig, Severity,
|
Annotation, Diagnostic, DiagnosticFormat, DiagnosticId, DisplayDiagnosticConfig, Severity,
|
||||||
Span, SubDiagnostic,
|
Span, SubDiagnostic, SubDiagnosticSeverity,
|
||||||
};
|
};
|
||||||
use ruff_db::files::system_path_to_file;
|
use ruff_db::files::system_path_to_file;
|
||||||
use ruff_db::system::{System, SystemPath, SystemPathBuf};
|
use ruff_db::system::{System, SystemPath, SystemPathBuf};
|
||||||
|
@ -318,7 +318,7 @@ impl Options {
|
||||||
|
|
||||||
if self.environment.or_default().root.is_some() {
|
if self.environment.or_default().root.is_some() {
|
||||||
diagnostic = diagnostic.sub(SubDiagnostic::new(
|
diagnostic = diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"The `src.root` setting was ignored in favor of the `environment.root` setting",
|
"The `src.root` setting was ignored in favor of the `environment.root` setting",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -811,7 +811,7 @@ fn build_include_filter(
|
||||||
Severity::Warning,
|
Severity::Warning,
|
||||||
)
|
)
|
||||||
.sub(SubDiagnostic::new(
|
.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"Remove the `include` option to match all files or add a pattern to match specific files",
|
"Remove the `include` option to match all files or add a pattern to match specific files",
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -853,13 +853,13 @@ fn build_include_filter(
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
diagnostic.sub(SubDiagnostic::new(
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format!("The pattern is defined in the `{}` option in your configuration file", context.include_name()),
|
format!("The pattern is defined in the `{}` option in your configuration file", context.include_name()),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ValueSource::Cli => diagnostic.sub(SubDiagnostic::new(
|
ValueSource::Cli => diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"The pattern was specified on the CLI",
|
"The pattern was specified on the CLI",
|
||||||
)),
|
)),
|
||||||
ValueSource::PythonVSCodeExtension => unreachable!("Can't configure includes from the Python VSCode extension"),
|
ValueSource::PythonVSCodeExtension => unreachable!("Can't configure includes from the Python VSCode extension"),
|
||||||
|
@ -883,7 +883,7 @@ fn build_include_filter(
|
||||||
Severity::Error,
|
Severity::Error,
|
||||||
);
|
);
|
||||||
Box::new(diagnostic.sub(SubDiagnostic::new(
|
Box::new(diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"Please open an issue on the ty repository and share the patterns that caused the error.",
|
"Please open an issue on the ty repository and share the patterns that caused the error.",
|
||||||
)))
|
)))
|
||||||
})
|
})
|
||||||
|
@ -936,13 +936,13 @@ fn build_exclude_filter(
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
diagnostic.sub(SubDiagnostic::new(
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format!("The pattern is defined in the `{}` option in your configuration file", context.exclude_name()),
|
format!("The pattern is defined in the `{}` option in your configuration file", context.exclude_name()),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ValueSource::Cli => diagnostic.sub(SubDiagnostic::new(
|
ValueSource::Cli => diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"The pattern was specified on the CLI",
|
"The pattern was specified on the CLI",
|
||||||
)),
|
)),
|
||||||
ValueSource::PythonVSCodeExtension => unreachable!(
|
ValueSource::PythonVSCodeExtension => unreachable!(
|
||||||
|
@ -960,7 +960,7 @@ fn build_exclude_filter(
|
||||||
Severity::Error,
|
Severity::Error,
|
||||||
);
|
);
|
||||||
Box::new(diagnostic.sub(SubDiagnostic::new(
|
Box::new(diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"Please open an issue on the ty repository and share the patterns that caused the error.",
|
"Please open an issue on the ty repository and share the patterns that caused the error.",
|
||||||
)))
|
)))
|
||||||
})
|
})
|
||||||
|
@ -1197,26 +1197,26 @@ impl RangedValue<OverrideOptions> {
|
||||||
|
|
||||||
diagnostic = if self.rules.is_none() {
|
diagnostic = if self.rules.is_none() {
|
||||||
diagnostic = diagnostic.sub(SubDiagnostic::new(
|
diagnostic = diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"It has no `rules` table",
|
"It has no `rules` table",
|
||||||
));
|
));
|
||||||
diagnostic.sub(SubDiagnostic::new(
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"Add a `[overrides.rules]` table...",
|
"Add a `[overrides.rules]` table...",
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
diagnostic = diagnostic.sub(SubDiagnostic::new(
|
diagnostic = diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"The rules table is empty",
|
"The rules table is empty",
|
||||||
));
|
));
|
||||||
diagnostic.sub(SubDiagnostic::new(
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"Add a rule to `[overrides.rules]` to override specific rules...",
|
"Add a rule to `[overrides.rules]` to override specific rules...",
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
diagnostic = diagnostic.sub(SubDiagnostic::new(
|
diagnostic = diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"or remove the `[[overrides]]` section if there's nothing to override",
|
"or remove the `[[overrides]]` section if there's nothing to override",
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -1251,23 +1251,23 @@ impl RangedValue<OverrideOptions> {
|
||||||
|
|
||||||
diagnostic = if self.exclude.is_none() {
|
diagnostic = if self.exclude.is_none() {
|
||||||
diagnostic.sub(SubDiagnostic::new(
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"It has no `include` or `exclude` option restricting the files",
|
"It has no `include` or `exclude` option restricting the files",
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
diagnostic.sub(SubDiagnostic::new(
|
diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"It has no `include` option and `exclude` is empty",
|
"It has no `include` option and `exclude` is empty",
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
diagnostic = diagnostic.sub(SubDiagnostic::new(
|
diagnostic = diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"Restrict the files by adding a pattern to `include` or `exclude`...",
|
"Restrict the files by adding a pattern to `include` or `exclude`...",
|
||||||
));
|
));
|
||||||
|
|
||||||
diagnostic = diagnostic.sub(SubDiagnostic::new(
|
diagnostic = diagnostic.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"or remove the `[[overrides]]` section and merge the configuration into the root `[rules]` table if the configuration should apply to all files",
|
"or remove the `[[overrides]]` section and merge the configuration into the root `[rules]` table if the configuration should apply to all files",
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ use diagnostic::{
|
||||||
INVALID_CONTEXT_MANAGER, INVALID_SUPER_ARGUMENT, NOT_ITERABLE, POSSIBLY_UNBOUND_IMPLICIT_CALL,
|
INVALID_CONTEXT_MANAGER, INVALID_SUPER_ARGUMENT, NOT_ITERABLE, POSSIBLY_UNBOUND_IMPLICIT_CALL,
|
||||||
UNAVAILABLE_IMPLICIT_SUPER_ARGUMENTS,
|
UNAVAILABLE_IMPLICIT_SUPER_ARGUMENTS,
|
||||||
};
|
};
|
||||||
use ruff_db::diagnostic::{Annotation, Diagnostic, Severity, Span, SubDiagnostic};
|
use ruff_db::diagnostic::{Annotation, Diagnostic, Span, SubDiagnostic, SubDiagnosticSeverity};
|
||||||
use ruff_db::files::File;
|
use ruff_db::files::File;
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::{self as ast, AnyNodeRef};
|
use ruff_python_ast::{self as ast, AnyNodeRef};
|
||||||
|
@ -7077,7 +7077,7 @@ impl<'db> BoolError<'db> {
|
||||||
not_boolable_type.display(context.db())
|
not_boolable_type.display(context.db())
|
||||||
));
|
));
|
||||||
let mut sub = SubDiagnostic::new(
|
let mut sub = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"`__bool__` methods must only have a `self` parameter",
|
"`__bool__` methods must only have a `self` parameter",
|
||||||
);
|
);
|
||||||
if let Some((func_span, parameter_span)) = not_boolable_type
|
if let Some((func_span, parameter_span)) = not_boolable_type
|
||||||
|
@ -7102,7 +7102,7 @@ impl<'db> BoolError<'db> {
|
||||||
not_boolable = not_boolable_type.display(context.db()),
|
not_boolable = not_boolable_type.display(context.db()),
|
||||||
));
|
));
|
||||||
let mut sub = SubDiagnostic::new(
|
let mut sub = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format_args!(
|
format_args!(
|
||||||
"`{return_type}` is not assignable to `bool`",
|
"`{return_type}` is not assignable to `bool`",
|
||||||
return_type = return_type.display(context.db()),
|
return_type = return_type.display(context.db()),
|
||||||
|
@ -7128,7 +7128,7 @@ impl<'db> BoolError<'db> {
|
||||||
not_boolable_type.display(context.db())
|
not_boolable_type.display(context.db())
|
||||||
));
|
));
|
||||||
let sub = SubDiagnostic::new(
|
let sub = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format_args!(
|
format_args!(
|
||||||
"`__bool__` on `{}` must be callable",
|
"`__bool__` on `{}` must be callable",
|
||||||
not_boolable_type.display(context.db())
|
not_boolable_type.display(context.db())
|
||||||
|
|
|
@ -30,7 +30,7 @@ use crate::types::{
|
||||||
MethodWrapperKind, PropertyInstanceType, SpecialFormType, TypeMapping, UnionType,
|
MethodWrapperKind, PropertyInstanceType, SpecialFormType, TypeMapping, UnionType,
|
||||||
WrapperDescriptorKind, enums, ide_support, todo_type,
|
WrapperDescriptorKind, enums, ide_support, todo_type,
|
||||||
};
|
};
|
||||||
use ruff_db::diagnostic::{Annotation, Diagnostic, Severity, SubDiagnostic};
|
use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
|
|
||||||
/// Binding information for a possible union of callables. At a call site, the arguments must be
|
/// Binding information for a possible union of callables. At a call site, the arguments must be
|
||||||
|
@ -1668,8 +1668,10 @@ impl<'db> CallableBinding<'db> {
|
||||||
.first()
|
.first()
|
||||||
.and_then(|overload| overload.spans(context.db()))
|
.and_then(|overload| overload.spans(context.db()))
|
||||||
{
|
{
|
||||||
let mut sub =
|
let mut sub = SubDiagnostic::new(
|
||||||
SubDiagnostic::new(Severity::Info, "First overload defined here");
|
SubDiagnosticSeverity::Info,
|
||||||
|
"First overload defined here",
|
||||||
|
);
|
||||||
sub.annotate(Annotation::primary(spans.signature));
|
sub.annotate(Annotation::primary(spans.signature));
|
||||||
diag.sub(sub);
|
diag.sub(sub);
|
||||||
}
|
}
|
||||||
|
@ -1696,7 +1698,7 @@ impl<'db> CallableBinding<'db> {
|
||||||
implementation.and_then(|function| function.spans(context.db()))
|
implementation.and_then(|function| function.spans(context.db()))
|
||||||
{
|
{
|
||||||
let mut sub = SubDiagnostic::new(
|
let mut sub = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"Overload implementation defined here",
|
"Overload implementation defined here",
|
||||||
);
|
);
|
||||||
sub.annotate(Annotation::primary(spans.signature));
|
sub.annotate(Annotation::primary(spans.signature));
|
||||||
|
@ -2570,8 +2572,10 @@ impl<'db> BindingError<'db> {
|
||||||
overload.parameter_span(context.db(), Some(parameter.index))
|
overload.parameter_span(context.db(), Some(parameter.index))
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
let mut sub =
|
let mut sub = SubDiagnostic::new(
|
||||||
SubDiagnostic::new(Severity::Info, "Matching overload defined here");
|
SubDiagnosticSeverity::Info,
|
||||||
|
"Matching overload defined here",
|
||||||
|
);
|
||||||
sub.annotate(Annotation::primary(name_span));
|
sub.annotate(Annotation::primary(name_span));
|
||||||
sub.annotate(
|
sub.annotate(
|
||||||
Annotation::secondary(parameter_span)
|
Annotation::secondary(parameter_span)
|
||||||
|
@ -2607,7 +2611,8 @@ impl<'db> BindingError<'db> {
|
||||||
} else if let Some((name_span, parameter_span)) =
|
} else if let Some((name_span, parameter_span)) =
|
||||||
callable_ty.parameter_span(context.db(), Some(parameter.index))
|
callable_ty.parameter_span(context.db(), Some(parameter.index))
|
||||||
{
|
{
|
||||||
let mut sub = SubDiagnostic::new(Severity::Info, "Function defined here");
|
let mut sub =
|
||||||
|
SubDiagnostic::new(SubDiagnosticSeverity::Info, "Function defined here");
|
||||||
sub.annotate(Annotation::primary(name_span));
|
sub.annotate(Annotation::primary(name_span));
|
||||||
sub.annotate(
|
sub.annotate(
|
||||||
Annotation::secondary(parameter_span).message("Parameter declared here"),
|
Annotation::secondary(parameter_span).message("Parameter declared here"),
|
||||||
|
@ -2733,7 +2738,10 @@ impl<'db> BindingError<'db> {
|
||||||
let module = parsed_module(context.db(), typevar_definition.file(context.db()))
|
let module = parsed_module(context.db(), typevar_definition.file(context.db()))
|
||||||
.load(context.db());
|
.load(context.db());
|
||||||
let typevar_range = typevar_definition.full_range(context.db(), &module);
|
let typevar_range = typevar_definition.full_range(context.db(), &module);
|
||||||
let mut sub = SubDiagnostic::new(Severity::Info, "Type variable defined here");
|
let mut sub = SubDiagnostic::new(
|
||||||
|
SubDiagnosticSeverity::Info,
|
||||||
|
"Type variable defined here",
|
||||||
|
);
|
||||||
sub.annotate(Annotation::primary(typevar_range.into()));
|
sub.annotate(Annotation::primary(typevar_range.into()));
|
||||||
diag.sub(sub);
|
diag.sub(sub);
|
||||||
}
|
}
|
||||||
|
@ -2801,7 +2809,7 @@ impl UnionDiagnostic<'_, '_> {
|
||||||
/// diagnostic.
|
/// diagnostic.
|
||||||
fn add_union_context(&self, db: &'_ dyn Db, diag: &mut Diagnostic) {
|
fn add_union_context(&self, db: &'_ dyn Db, diag: &mut Diagnostic) {
|
||||||
let sub = SubDiagnostic::new(
|
let sub = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format_args!(
|
format_args!(
|
||||||
"Union variant `{callable_ty}` is incompatible with this call site",
|
"Union variant `{callable_ty}` is incompatible with this call site",
|
||||||
callable_ty = self.binding.callable_type.display(db),
|
callable_ty = self.binding.callable_type.display(db),
|
||||||
|
@ -2810,7 +2818,7 @@ impl UnionDiagnostic<'_, '_> {
|
||||||
diag.sub(sub);
|
diag.sub(sub);
|
||||||
|
|
||||||
let sub = SubDiagnostic::new(
|
let sub = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format_args!(
|
format_args!(
|
||||||
"Attempted to call union type `{}`",
|
"Attempted to call union type `{}`",
|
||||||
self.callable_type.display(db)
|
self.callable_type.display(db)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use drop_bomb::DebugDropBomb;
|
use drop_bomb::DebugDropBomb;
|
||||||
use ruff_db::diagnostic::{DiagnosticTag, SubDiagnostic};
|
use ruff_db::diagnostic::{DiagnosticTag, SubDiagnostic, SubDiagnosticSeverity};
|
||||||
use ruff_db::parsed::ParsedModuleRef;
|
use ruff_db::parsed::ParsedModuleRef;
|
||||||
use ruff_db::{
|
use ruff_db::{
|
||||||
diagnostic::{Annotation, Diagnostic, DiagnosticId, IntoDiagnosticMessage, Severity, Span},
|
diagnostic::{Annotation, Diagnostic, DiagnosticId, IntoDiagnosticMessage, Severity, Span},
|
||||||
|
@ -330,7 +330,7 @@ impl Drop for LintDiagnosticGuard<'_, '_> {
|
||||||
let mut diag = self.diag.take().unwrap();
|
let mut diag = self.diag.take().unwrap();
|
||||||
|
|
||||||
diag.sub(SubDiagnostic::new(
|
diag.sub(SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
match self.source {
|
match self.source {
|
||||||
LintSource::Default => format!("rule `{}` is enabled by default", diag.id()),
|
LintSource::Default => format!("rule `{}` is enabled by default", diag.id()),
|
||||||
LintSource::Cli => format!("rule `{}` was selected on the command line", diag.id()),
|
LintSource::Cli => format!("rule `{}` was selected on the command line", diag.id()),
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::types::{SpecialFormType, Type, protocol_class::ProtocolClassLiteral};
|
||||||
use crate::util::diagnostics::format_enumeration;
|
use crate::util::diagnostics::format_enumeration;
|
||||||
use crate::{Db, FxIndexMap, Module, ModuleName, Program, declare_lint};
|
use crate::{Db, FxIndexMap, Module, ModuleName, Program, declare_lint};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ruff_db::diagnostic::{Annotation, Diagnostic, Severity, SubDiagnostic};
|
use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity};
|
||||||
use ruff_python_ast::{self as ast, AnyNodeRef};
|
use ruff_python_ast::{self as ast, AnyNodeRef};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
@ -1929,7 +1929,7 @@ pub(super) fn report_implicit_return_type(
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut sub_diagnostic = SubDiagnostic::new(
|
let mut sub_diagnostic = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"Only classes that directly inherit from `typing.Protocol` \
|
"Only classes that directly inherit from `typing.Protocol` \
|
||||||
or `typing_extensions.Protocol` are considered protocol classes",
|
or `typing_extensions.Protocol` are considered protocol classes",
|
||||||
);
|
);
|
||||||
|
@ -2037,7 +2037,7 @@ pub(crate) fn report_instance_layout_conflict(
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut subdiagnostic = SubDiagnostic::new(
|
let mut subdiagnostic = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
"Two classes cannot coexist in a class's MRO if their instances \
|
"Two classes cannot coexist in a class's MRO if their instances \
|
||||||
have incompatible memory layouts",
|
have incompatible memory layouts",
|
||||||
);
|
);
|
||||||
|
@ -2230,7 +2230,7 @@ pub(crate) fn report_bad_argument_to_get_protocol_members(
|
||||||
diagnostic.info("Only protocol classes can be passed to `get_protocol_members`");
|
diagnostic.info("Only protocol classes can be passed to `get_protocol_members`");
|
||||||
|
|
||||||
let mut class_def_diagnostic = SubDiagnostic::new(
|
let mut class_def_diagnostic = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format_args!(
|
format_args!(
|
||||||
"`{}` is declared here, but it is not a protocol class:",
|
"`{}` is declared here, but it is not a protocol class:",
|
||||||
class.name(db)
|
class.name(db)
|
||||||
|
@ -2292,7 +2292,7 @@ pub(crate) fn report_runtime_check_against_non_runtime_checkable_protocol(
|
||||||
diagnostic.set_primary_message("This call will raise `TypeError` at runtime");
|
diagnostic.set_primary_message("This call will raise `TypeError` at runtime");
|
||||||
|
|
||||||
let mut class_def_diagnostic = SubDiagnostic::new(
|
let mut class_def_diagnostic = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format_args!(
|
format_args!(
|
||||||
"`{class_name}` is declared as a protocol class, \
|
"`{class_name}` is declared as a protocol class, \
|
||||||
but it is not declared as runtime-checkable"
|
but it is not declared as runtime-checkable"
|
||||||
|
@ -2326,7 +2326,7 @@ pub(crate) fn report_attempted_protocol_instantiation(
|
||||||
diagnostic.set_primary_message("This call will raise `TypeError` at runtime");
|
diagnostic.set_primary_message("This call will raise `TypeError` at runtime");
|
||||||
|
|
||||||
let mut class_def_diagnostic = SubDiagnostic::new(
|
let mut class_def_diagnostic = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format_args!("Protocol classes cannot be instantiated"),
|
format_args!("Protocol classes cannot be instantiated"),
|
||||||
);
|
);
|
||||||
class_def_diagnostic.annotate(
|
class_def_diagnostic.annotate(
|
||||||
|
@ -2360,7 +2360,7 @@ pub(crate) fn report_duplicate_bases(
|
||||||
builder.into_diagnostic(format_args!("Duplicate base class `{duplicate_name}`",));
|
builder.into_diagnostic(format_args!("Duplicate base class `{duplicate_name}`",));
|
||||||
|
|
||||||
let mut sub_diagnostic = SubDiagnostic::new(
|
let mut sub_diagnostic = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format_args!(
|
format_args!(
|
||||||
"The definition of class `{}` will raise `TypeError` at runtime",
|
"The definition of class `{}` will raise `TypeError` at runtime",
|
||||||
class.name(db)
|
class.name(db)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{Db, Program, PythonVersionWithSource};
|
use crate::{Db, Program, PythonVersionWithSource};
|
||||||
use ruff_db::diagnostic::{Annotation, Diagnostic, Severity, SubDiagnostic};
|
use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
/// Add a subdiagnostic to `diagnostic` that explains why a certain Python version was inferred.
|
/// Add a subdiagnostic to `diagnostic` that explains why a certain Python version was inferred.
|
||||||
|
@ -23,7 +23,7 @@ pub fn add_inferred_python_version_hint_to_diagnostic(
|
||||||
crate::PythonVersionSource::ConfigFile(source) => {
|
crate::PythonVersionSource::ConfigFile(source) => {
|
||||||
if let Some(span) = source.span(db) {
|
if let Some(span) = source.span(db) {
|
||||||
let mut sub_diagnostic = SubDiagnostic::new(
|
let mut sub_diagnostic = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format_args!("Python {version} was assumed when {action}"),
|
format_args!("Python {version} was assumed when {action}"),
|
||||||
);
|
);
|
||||||
sub_diagnostic.annotate(Annotation::primary(span).message(format_args!(
|
sub_diagnostic.annotate(Annotation::primary(span).message(format_args!(
|
||||||
|
@ -39,7 +39,7 @@ pub fn add_inferred_python_version_hint_to_diagnostic(
|
||||||
crate::PythonVersionSource::PyvenvCfgFile(source) => {
|
crate::PythonVersionSource::PyvenvCfgFile(source) => {
|
||||||
if let Some(span) = source.span(db) {
|
if let Some(span) = source.span(db) {
|
||||||
let mut sub_diagnostic = SubDiagnostic::new(
|
let mut sub_diagnostic = SubDiagnostic::new(
|
||||||
Severity::Info,
|
SubDiagnosticSeverity::Info,
|
||||||
format_args!(
|
format_args!(
|
||||||
"Python {version} was assumed when {action} because of your virtual environment"
|
"Python {version} was assumed when {action} because of your virtual environment"
|
||||||
),
|
),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue