Files
ruff/crates/ruff_dev/src/main.rs
konstin b8d378b0a3 Add a script that tests formatter stability on repositories (#5055)
## Summary

We want to ensure that once formatted content stays the same when
formatted again, which is known as formatter stability or formatter
idempotency, and that the formatter prints syntactically valid code. As
our test cases cover only a limited amount of code, this allows checking
entire repositories.

This adds a new subcommand to `ruff_dev` which can be invoked as `cargo
run --bin ruff_dev -- check-formatter-stability <repo>`. While initially
only intended to check stability, it has also found cases where the
formatter printed invalid syntax or panicked.

 ## Test Plan

Running this on cpython is already identifying bugs
(https://github.com/astral-sh/ruff/pull/5089)
2023-06-19 14:13:38 +00:00

103 lines
3.5 KiB
Rust

//! This crate implements an internal CLI for developers of Ruff.
//!
//! Within the ruff repository you can run it with `cargo dev`.
use anyhow::Result;
use clap::{Parser, Subcommand};
use ruff::logging::{set_up_logging, LogLevel};
use ruff_cli::check;
use std::process::ExitCode;
mod check_formatter_stability;
mod generate_all;
mod generate_cli_help;
mod generate_docs;
mod generate_json_schema;
mod generate_options;
mod generate_rules_table;
mod print_ast;
mod print_cst;
mod print_tokens;
mod round_trip;
const ROOT_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../");
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
struct Args {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
#[allow(clippy::large_enum_variant)]
enum Command {
/// Run all code and documentation generation steps.
GenerateAll(generate_all::Args),
/// Generate JSON schema for the TOML configuration file.
GenerateJSONSchema(generate_json_schema::Args),
/// Generate a Markdown-compatible table of supported lint rules.
GenerateRulesTable,
/// Generate a Markdown-compatible listing of configuration options.
GenerateOptions,
/// Generate CLI help.
GenerateCliHelp(generate_cli_help::Args),
/// Generate Markdown docs.
GenerateDocs(generate_docs::Args),
/// Print the AST for a given Python file.
PrintAST(print_ast::Args),
/// Print the LibCST CST for a given Python file.
PrintCST(print_cst::Args),
/// Print the token stream for a given Python file.
PrintTokens(print_tokens::Args),
/// Run round-trip source code generation on a given Python file.
RoundTrip(round_trip::Args),
/// Run a ruff command n times for profiling/benchmarking
Repeat {
#[clap(flatten)]
args: ruff_cli::args::CheckArgs,
#[clap(flatten)]
log_level_args: ruff_cli::args::LogLevelArgs,
/// Run this many times
#[clap(long, short = 'n')]
repeat: usize,
},
/// Format a repository twice and ensure that it looks that the first and second formatting
/// look the same. Same arguments as `ruff check`
CheckFormatterStability(check_formatter_stability::Args),
}
fn main() -> Result<ExitCode> {
let args = Args::parse();
#[allow(clippy::print_stdout)]
match args.command {
Command::GenerateAll(args) => generate_all::main(&args)?,
Command::GenerateJSONSchema(args) => generate_json_schema::main(&args)?,
Command::GenerateRulesTable => println!("{}", generate_rules_table::generate()),
Command::GenerateOptions => println!("{}", generate_options::generate()),
Command::GenerateCliHelp(args) => generate_cli_help::main(&args)?,
Command::GenerateDocs(args) => generate_docs::main(&args)?,
Command::PrintAST(args) => print_ast::main(&args)?,
Command::PrintCST(args) => print_cst::main(&args)?,
Command::PrintTokens(args) => print_tokens::main(&args)?,
Command::RoundTrip(args) => round_trip::main(&args)?,
Command::Repeat {
args,
repeat,
log_level_args,
} => {
let log_level = LogLevel::from(&log_level_args);
set_up_logging(&log_level)?;
for _ in 0..repeat {
check(args.clone(), log_level)?;
}
}
Command::CheckFormatterStability(args) => {
let exit_code = check_formatter_stability::main(&args)?;
return Ok(exit_code);
}
}
Ok(ExitCode::SUCCESS)
}