mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
parent
945a9e187c
commit
f41796d559
4 changed files with 115 additions and 1 deletions
|
@ -211,6 +211,15 @@ pub struct CheckArgs {
|
|||
update_check: bool,
|
||||
#[clap(long, overrides_with("update_check"), hide = true)]
|
||||
no_update_check: bool,
|
||||
/// Show counts for every rule with at least one violation.
|
||||
#[arg(
|
||||
long,
|
||||
// Unsupported default-command arguments.
|
||||
conflicts_with = "diff",
|
||||
conflicts_with = "show_source",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub statistics: bool,
|
||||
/// Enable automatic additions of `noqa` directives to failing lines.
|
||||
#[arg(
|
||||
long,
|
||||
|
@ -218,6 +227,7 @@ pub struct CheckArgs {
|
|||
conflicts_with = "show_files",
|
||||
conflicts_with = "show_settings",
|
||||
// Unsupported default-command arguments.
|
||||
conflicts_with = "statistics",
|
||||
conflicts_with = "stdin_filename",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
|
@ -230,6 +240,7 @@ pub struct CheckArgs {
|
|||
// conflicts_with = "show_files",
|
||||
conflicts_with = "show_settings",
|
||||
// Unsupported default-command arguments.
|
||||
conflicts_with = "statistics",
|
||||
conflicts_with = "stdin_filename",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
|
@ -242,6 +253,7 @@ pub struct CheckArgs {
|
|||
conflicts_with = "show_files",
|
||||
// conflicts_with = "show_settings",
|
||||
// Unsupported default-command arguments.
|
||||
conflicts_with = "statistics",
|
||||
conflicts_with = "stdin_filename",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
|
@ -316,6 +328,7 @@ impl CheckArgs {
|
|||
no_cache: self.no_cache,
|
||||
show_files: self.show_files,
|
||||
show_settings: self.show_settings,
|
||||
statistics: self.statistics,
|
||||
stdin_filename: self.stdin_filename,
|
||||
watch: self.watch,
|
||||
},
|
||||
|
@ -371,6 +384,7 @@ pub struct Arguments {
|
|||
pub no_cache: bool,
|
||||
pub show_files: bool,
|
||||
pub show_settings: bool,
|
||||
pub statistics: bool,
|
||||
pub stdin_filename: Option<PathBuf>,
|
||||
pub watch: bool,
|
||||
}
|
||||
|
|
|
@ -244,7 +244,11 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitCode> {
|
|||
// unless we're writing fixes via stdin (in which case, the transformed
|
||||
// source code goes to stdout).
|
||||
if !(is_stdin && matches!(autofix, fix::FixMode::Apply | fix::FixMode::Diff)) {
|
||||
printer.write_once(&diagnostics)?;
|
||||
if cli.statistics {
|
||||
printer.write_statistics(&diagnostics)?;
|
||||
} else {
|
||||
printer.write_once(&diagnostics)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for updates if we're in a non-silent log level.
|
||||
|
|
|
@ -43,6 +43,13 @@ struct ExpandedMessage<'a> {
|
|||
filename: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ExpandedStatistics<'a> {
|
||||
count: usize,
|
||||
code: &'a str,
|
||||
message: String,
|
||||
}
|
||||
|
||||
struct SerializeRuleAsCode<'a>(&'a Rule);
|
||||
|
||||
impl Serialize for SerializeRuleAsCode<'_> {
|
||||
|
@ -336,6 +343,77 @@ impl<'a> Printer<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_statistics(&self, diagnostics: &Diagnostics) -> Result<()> {
|
||||
let mut violations = diagnostics
|
||||
.messages
|
||||
.iter()
|
||||
.map(|message| message.kind.rule())
|
||||
.collect::<Vec<_>>();
|
||||
violations.sort();
|
||||
violations.dedup();
|
||||
|
||||
let statistics = violations
|
||||
.iter()
|
||||
.map(|rule| ExpandedStatistics {
|
||||
code: rule.code(),
|
||||
count: diagnostics
|
||||
.messages
|
||||
.iter()
|
||||
.filter(|message| message.kind.rule() == *rule)
|
||||
.count(),
|
||||
message: diagnostics
|
||||
.messages
|
||||
.iter()
|
||||
.find(|message| message.kind.rule() == *rule)
|
||||
.map(|message| message.kind.body())
|
||||
.unwrap(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut stdout = BufWriter::new(io::stdout().lock());
|
||||
match self.format {
|
||||
SerializationFormat::Text => {
|
||||
// Compute the maximum number of digits in the count and code, for all messages, to enable
|
||||
// pretty-printing.
|
||||
let count_width = num_digits(
|
||||
statistics
|
||||
.iter()
|
||||
.map(|statistic| statistic.count)
|
||||
.max()
|
||||
.unwrap(),
|
||||
);
|
||||
let code_width = statistics
|
||||
.iter()
|
||||
.map(|statistic| statistic.code.len())
|
||||
.max()
|
||||
.unwrap();
|
||||
|
||||
// By default, we mimic Flake8's `--statistics` format.
|
||||
for msg in statistics {
|
||||
writeln!(
|
||||
stdout,
|
||||
"{:>count_width$}\t{:<code_width$}\t{}",
|
||||
msg.count, msg.code, msg.message
|
||||
)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
SerializationFormat::Json => {
|
||||
writeln!(stdout, "{}", serde_json::to_string_pretty(&statistics)?)?;
|
||||
}
|
||||
_ => {
|
||||
anyhow::bail!(
|
||||
"Unsupported serialization format for statistics: {:?}",
|
||||
self.format
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
stdout.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_continuously(&self, diagnostics: &Diagnostics) -> Result<()> {
|
||||
if matches!(self.log_level, LogLevel::Silent) {
|
||||
return Ok(());
|
||||
|
|
|
@ -182,3 +182,21 @@ fn explain_status_codes() -> Result<()> {
|
|||
cmd.args(["--explain", "RUF404"]).assert().failure();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_statistics() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
let output = cmd
|
||||
.args(["-", "--format", "text", "--select", "F401", "--statistics"])
|
||||
.write_stdin("import sys\nimport os\n\nprint(os.getuid())\n")
|
||||
.assert()
|
||||
.failure();
|
||||
assert_eq!(
|
||||
str::from_utf8(&output.get_output().stdout)?
|
||||
.lines()
|
||||
.last()
|
||||
.unwrap(),
|
||||
"1\tF401\t`sys` imported but unused"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue