Skip to content

Commit ab2d3c2

Browse files
committed
Avoid allocating a new string when printing annotations
1 parent 34e9786 commit ab2d3c2

29 files changed

+1458
-593
lines changed

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ruff/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ ruff_python_semantic = { path = "../ruff_python_semantic" }
2222
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
2323
ruff_rustpython = { path = "../ruff_rustpython" }
2424

25+
annotate-snippets = { version = "0.9.1", features = ["color"] }
2526
anyhow = { workspace = true }
2627
bitflags = { workspace = true }
2728
chrono = { workspace = true }
@@ -48,6 +49,7 @@ path-absolutize = { workspace = true, features = [
4849
] }
4950
pathdiff = { version = "0.2.1" }
5051
pep440_rs = { version = "0.3.1", features = ["serde"] }
52+
quick-junit = { version = "0.3.2" }
5153
regex = { workspace = true }
5254
result-like = { version = "0.4.6" }
5355
rustc-hash = { workspace = true }

crates/ruff/src/autofix/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@ pub mod actions;
1515

1616
/// Auto-fix errors in a file, and write the fixed source code to disk.
1717
pub fn fix_file(diagnostics: &[Diagnostic], locator: &Locator) -> Option<(String, FixTable)> {
18-
if diagnostics.iter().all(|check| check.fix.is_empty()) {
18+
let mut with_fixes = diagnostics
19+
.iter()
20+
.filter(|diag| !diag.fix.is_empty())
21+
.peekable();
22+
23+
if with_fixes.peek().is_none() {
1924
None
2025
} else {
21-
Some(apply_fixes(diagnostics.iter(), locator))
26+
Some(apply_fixes(with_fixes, locator))
2227
}
2328
}
2429

crates/ruff/src/message.rs

Lines changed: 0 additions & 89 deletions
This file was deleted.

crates/ruff/src/message/azure.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use crate::message::{Emitter, EmitterContext, Message};
2+
use crate::registry::AsRule;
3+
use std::io::Write;
4+
5+
/// Generate error logging commands for Azure Pipelines format.
6+
/// See [documentation](https://learn.microsoft.com/en-us/azure/devops/pipelines/scripts/logging-commands?view=azure-devops&tabs=bash#logissue-log-an-error-or-warning)
7+
#[derive(Default)]
8+
pub struct AzureEmitter;
9+
10+
impl Emitter for AzureEmitter {
11+
fn emit(
12+
&mut self,
13+
writer: &mut dyn Write,
14+
messages: &[Message],
15+
context: &EmitterContext,
16+
) -> anyhow::Result<()> {
17+
for message in messages {
18+
let (line, col) = if context.is_jupyter_notebook(&message.filename) {
19+
// We can't give a reasonable location for the structured formats,
20+
// so we show one that's clearly a fallback
21+
(1, 0)
22+
} else {
23+
(message.location.row(), message.location.column())
24+
};
25+
26+
writeln!(
27+
writer,
28+
"##vso[task.logissue type=error\
29+
;sourcepath={filename};linenumber={line};columnnumber={col};code={code};]{body}",
30+
filename = message.filename,
31+
code = message.kind.rule().noqa_code(),
32+
body = message.kind.body,
33+
)?;
34+
}
35+
36+
Ok(())
37+
}
38+
}
39+
40+
#[cfg(test)]
41+
mod tests {
42+
use crate::message::tests::{capture_emitter_output, create_messages};
43+
use crate::message::AzureEmitter;
44+
use insta::assert_snapshot;
45+
46+
#[test]
47+
fn output() {
48+
let mut emitter = AzureEmitter::default();
49+
let content = capture_emitter_output(&mut emitter, &create_messages());
50+
51+
assert_snapshot!(content);
52+
}
53+
}

crates/ruff/src/message/github.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use crate::fs::relativize_path;
2+
use crate::message::{Emitter, EmitterContext, Message};
3+
use crate::registry::AsRule;
4+
use std::io::Write;
5+
6+
/// Generate error workflow command in GitHub Actions format.
7+
/// See: [GitHub documentation](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message)
8+
#[derive(Default)]
9+
pub struct GithubEmitter;
10+
11+
impl Emitter for GithubEmitter {
12+
fn emit(
13+
&mut self,
14+
writer: &mut dyn Write,
15+
messages: &[Message],
16+
context: &EmitterContext,
17+
) -> anyhow::Result<()> {
18+
for message in messages {
19+
let (row, column) = if context.is_jupyter_notebook(&message.filename) {
20+
// We can't give a reasonable location for the structured formats,
21+
// so we show one that's clearly a fallback
22+
(1, 0)
23+
} else {
24+
(message.location.row(), message.location.column())
25+
};
26+
27+
write!(
28+
writer,
29+
"::error title=Ruff \
30+
({code}),file={file},line={row},col={column},endLine={end_row},endColumn={end_column}::",
31+
code = message.kind.rule().noqa_code(),
32+
file = message.filename,
33+
row = message.location.row(),
34+
column = message.location.column(),
35+
end_row = message.end_location.row(),
36+
end_column = message.end_location.column(),
37+
)?;
38+
39+
writeln!(
40+
writer,
41+
"{path}:{row}:{column}: {code} {body}",
42+
path = relativize_path(&message.filename),
43+
code = message.kind.rule().noqa_code(),
44+
body = message.kind.body,
45+
)?;
46+
}
47+
48+
Ok(())
49+
}
50+
}
51+
52+
#[cfg(test)]
53+
mod tests {
54+
use crate::message::tests::{capture_emitter_output, create_messages};
55+
use crate::message::GithubEmitter;
56+
use insta::assert_snapshot;
57+
58+
#[test]
59+
fn output() {
60+
let mut emitter = GithubEmitter::default();
61+
let content = capture_emitter_output(&mut emitter, &create_messages());
62+
63+
assert_snapshot!(content);
64+
}
65+
}

0 commit comments

Comments
 (0)