Skip to content

Commit 0645418

Browse files
authored
Set the diagnostic URL for lint errors (#21514)
Summary -- This PR wires up the `Diagnostic::set_documentation_url` method from #21502 to Ruff's lint diagnostics. This enables the links for the full and concise output formats without any other changes. I considered also including the URLs for the grouped and pylint output formats, but the grouped format is still in `ruff_linter` instead of `ruff_db`, so we'd have to export some additional functionality to wire it up with `fmt_with_hyperlink`; and the pylint format doesn't currently render with color, so I think it might actually be machine readable rather than human readable? The other ouput formats (json, json-lines, junit, github, gitlab, rdjson, azure, sarif) seem more clearly not to need the links. Test Plan -- I guess you can't see my cursor or the browser opening, but it works for lint rules, which have links, and doesn't include a link for syntax errors, which don't have valid links. ![out](https://github.com/user-attachments/assets/a520c7f9-6d7b-4e5f-a1a9-3c5e21a51d3c)
1 parent 62343a1 commit 0645418

File tree

6 files changed

+36
-33
lines changed

6 files changed

+36
-33
lines changed

crates/ruff_db/src/diagnostic/mod.rs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -452,28 +452,6 @@ impl Diagnostic {
452452
.map(|sub| sub.inner.message.as_str())
453453
}
454454

455-
/// Returns the URL for the rule documentation, if it exists.
456-
pub fn to_ruff_url(&self) -> Option<String> {
457-
match self.id() {
458-
DiagnosticId::Panic
459-
| DiagnosticId::Io
460-
| DiagnosticId::InvalidSyntax
461-
| DiagnosticId::RevealedType
462-
| DiagnosticId::UnknownRule
463-
| DiagnosticId::InvalidGlob
464-
| DiagnosticId::EmptyInclude
465-
| DiagnosticId::UnnecessaryOverridesSection
466-
| DiagnosticId::UselessOverridesSection
467-
| DiagnosticId::DeprecatedSetting
468-
| DiagnosticId::Unformatted
469-
| DiagnosticId::InvalidCliOption
470-
| DiagnosticId::InternalError => None,
471-
DiagnosticId::Lint(lint_name) => {
472-
Some(format!("{}/rules/{lint_name}", env!("CARGO_PKG_HOMEPAGE")))
473-
}
474-
}
475-
}
476-
477455
/// Returns the filename for the message.
478456
///
479457
/// Panics if the diagnostic has no primary span, or if its file is not a `SourceFile`.

crates/ruff_db/src/diagnostic/render.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2881,6 +2881,12 @@ watermelon
28812881
self.diag.help(message);
28822882
self
28832883
}
2884+
2885+
/// Set the documentation URL for the diagnostic.
2886+
pub(super) fn documentation_url(mut self, url: impl Into<String>) -> DiagnosticBuilder<'e> {
2887+
self.diag.set_documentation_url(Some(url.into()));
2888+
self
2889+
}
28842890
}
28852891

28862892
/// A helper builder for tersely populating a `SubDiagnostic`.
@@ -2995,6 +3001,7 @@ def fibonacci(n):
29953001
TextSize::from(10),
29963002
))))
29973003
.noqa_offset(TextSize::from(7))
3004+
.documentation_url("https://docs.astral.sh/ruff/rules/unused-import")
29983005
.build(),
29993006
env.builder(
30003007
"unused-variable",
@@ -3009,11 +3016,13 @@ def fibonacci(n):
30093016
TextSize::from(99),
30103017
)))
30113018
.noqa_offset(TextSize::from(94))
3019+
.documentation_url("https://docs.astral.sh/ruff/rules/unused-variable")
30123020
.build(),
30133021
env.builder("undefined-name", Severity::Error, "Undefined name `a`")
30143022
.primary("undef.py", "1:3", "1:4", "")
30153023
.secondary_code("F821")
30163024
.noqa_offset(TextSize::from(3))
3025+
.documentation_url("https://docs.astral.sh/ruff/rules/undefined-name")
30173026
.build(),
30183027
];
30193028

@@ -3128,6 +3137,7 @@ if call(foo
31283137
TextSize::from(19),
31293138
))))
31303139
.noqa_offset(TextSize::from(16))
3140+
.documentation_url("https://docs.astral.sh/ruff/rules/unused-import")
31313141
.build(),
31323142
env.builder(
31333143
"unused-import",
@@ -3142,6 +3152,7 @@ if call(foo
31423152
TextSize::from(40),
31433153
))))
31443154
.noqa_offset(TextSize::from(35))
3155+
.documentation_url("https://docs.astral.sh/ruff/rules/unused-import")
31453156
.build(),
31463157
env.builder(
31473158
"unused-variable",
@@ -3156,6 +3167,7 @@ if call(foo
31563167
TextSize::from(104),
31573168
))))
31583169
.noqa_offset(TextSize::from(98))
3170+
.documentation_url("https://docs.astral.sh/ruff/rules/unused-variable")
31593171
.build(),
31603172
];
31613173

crates/ruff_db/src/diagnostic/render/json.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub(super) fn diagnostic_to_json<'a>(
100100
if config.preview {
101101
JsonDiagnostic {
102102
code: diagnostic.secondary_code_or_id(),
103-
url: diagnostic.to_ruff_url(),
103+
url: diagnostic.documentation_url(),
104104
message: diagnostic.body(),
105105
fix,
106106
cell: notebook_cell_index,
@@ -112,7 +112,7 @@ pub(super) fn diagnostic_to_json<'a>(
112112
} else {
113113
JsonDiagnostic {
114114
code: diagnostic.secondary_code_or_id(),
115-
url: diagnostic.to_ruff_url(),
115+
url: diagnostic.documentation_url(),
116116
message: diagnostic.body(),
117117
fix,
118118
cell: notebook_cell_index,
@@ -228,7 +228,7 @@ pub(crate) struct JsonDiagnostic<'a> {
228228
location: Option<JsonLocation>,
229229
message: &'a str,
230230
noqa_row: Option<OneIndexed>,
231-
url: Option<String>,
231+
url: Option<&'a str>,
232232
}
233233

234234
#[derive(Serialize)]
@@ -294,7 +294,10 @@ mod tests {
294294
env.format(DiagnosticFormat::Json);
295295
env.preview(false);
296296

297-
let diag = env.err().build();
297+
let diag = env
298+
.err()
299+
.documentation_url("https://docs.astral.sh/ruff/rules/test-diagnostic")
300+
.build();
298301

299302
insta::assert_snapshot!(
300303
env.render(&diag),
@@ -328,7 +331,10 @@ mod tests {
328331
env.format(DiagnosticFormat::Json);
329332
env.preview(true);
330333

331-
let diag = env.err().build();
334+
let diag = env
335+
.err()
336+
.documentation_url("https://docs.astral.sh/ruff/rules/test-diagnostic")
337+
.build();
332338

333339
insta::assert_snapshot!(
334340
env.render(&diag),

crates/ruff_db/src/diagnostic/render/rdjson.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ fn diagnostic_to_rdjson<'a>(
8282
value: diagnostic
8383
.secondary_code()
8484
.map_or_else(|| diagnostic.name(), |code| code.as_str()),
85-
url: diagnostic.to_ruff_url(),
85+
url: diagnostic.documentation_url(),
8686
},
8787
suggestions: rdjson_suggestions(
8888
edits,
@@ -182,7 +182,7 @@ impl RdjsonRange {
182182
#[derive(Serialize)]
183183
struct RdjsonCode<'a> {
184184
#[serde(skip_serializing_if = "Option::is_none")]
185-
url: Option<String>,
185+
url: Option<&'a str>,
186186
value: &'a str,
187187
}
188188

@@ -217,7 +217,10 @@ mod tests {
217217
env.format(DiagnosticFormat::Rdjson);
218218
env.preview(false);
219219

220-
let diag = env.err().build();
220+
let diag = env
221+
.err()
222+
.documentation_url("https://docs.astral.sh/ruff/rules/test-diagnostic")
223+
.build();
221224

222225
insta::assert_snapshot!(env.render(&diag));
223226
}
@@ -228,7 +231,10 @@ mod tests {
228231
env.format(DiagnosticFormat::Rdjson);
229232
env.preview(true);
230233

231-
let diag = env.err().build();
234+
let diag = env
235+
.err()
236+
.documentation_url("https://docs.astral.sh/ruff/rules/test-diagnostic")
237+
.build();
232238

233239
insta::assert_snapshot!(env.render(&diag));
234240
}

crates/ruff_linter/src/message/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ where
125125
}
126126

127127
diagnostic.set_secondary_code(SecondaryCode::new(rule.noqa_code().to_string()));
128+
diagnostic.set_documentation_url(rule.url());
128129

129130
diagnostic
130131
}

crates/ruff_server/src/lint.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,9 @@ fn to_lsp_diagnostic(
301301
severity,
302302
tags,
303303
code,
304-
code_description: diagnostic.to_ruff_url().and_then(|url| {
304+
code_description: diagnostic.documentation_url().and_then(|url| {
305305
Some(lsp_types::CodeDescription {
306-
href: lsp_types::Url::parse(&url).ok()?,
306+
href: lsp_types::Url::parse(url).ok()?,
307307
})
308308
}),
309309
source: Some(DIAGNOSTIC_NAME.into()),

0 commit comments

Comments
 (0)