diff --git a/src/bin/cargo-fixit/fixit.rs b/src/bin/cargo-fixit/fixit.rs index e178fa1..17ae5eb 100644 --- a/src/bin/cargo-fixit/fixit.rs +++ b/src/bin/cargo-fixit/fixit.rs @@ -6,12 +6,11 @@ use std::{ process::Stdio, }; -use cargo_fixit::{shell, CargoResult, CheckFlags}; +use cargo_fixit::{shell, CargoResult, CheckFlags, CheckMessage, Target}; use cargo_util::paths; use clap::Parser; use indexmap::{IndexMap, IndexSet}; -use rustfix::{collect_suggestions, diagnostics::Diagnostic, CodeFix}; -use serde::Deserialize; +use rustfix::{collect_suggestions, CodeFix}; use tracing::{trace, warn}; #[derive(Debug, Parser)] @@ -34,12 +33,6 @@ impl FixitArgs { } } -#[derive(Deserialize)] -struct CheckMessage { - message: Diagnostic, - package_id: String, -} - #[derive(Debug, Default)] struct File { fixes: u32, @@ -59,20 +52,19 @@ fn exec(args: FixitArgs) -> CargoResult<()> { let mut last_errors; - let mut current_package_id = None; + let mut current_target = None; let mut seen = HashSet::new(); loop { - let (errors, made_changes) = - run_rustfix(&args, &mut files, &mut current_package_id, &seen)?; + let (errors, made_changes) = run_rustfix(&args, &mut files, &mut current_target, &seen)?; last_errors = errors; iteration += 1; if !made_changes || iteration >= max_iterations { - if let Some(pkg) = current_package_id { + if let Some(pkg) = current_target { seen.insert(pkg); - current_package_id = None; + current_target = None; iteration = 0; } else { break; @@ -93,8 +85,8 @@ fn exec(args: FixitArgs) -> CargoResult<()> { fn run_rustfix( args: &FixitArgs, files: &mut IndexMap, - current_package_id: &mut Option, - seen: &HashSet, + current_target: &mut Option<(Target, String)>, + seen: &HashSet<(Target, String)>, ) -> CargoResult<(IndexSet, bool)> { let only = HashSet::new(); let mut file_map = IndexMap::new(); @@ -113,6 +105,7 @@ fn run_rustfix( for line in buf.lines() { let Ok(CheckMessage { + target, message: diagnostic, package_id, }) = serde_json::from_str(&line?) @@ -165,7 +158,9 @@ fn run_rustfix( } } - if seen.contains(&package_id) { + let target = (target.clone(), package_id.clone()); + + if seen.contains(&target) { trace!( "rejecting package id `{}` already seen: {:?}", package_id, @@ -177,9 +172,9 @@ fn run_rustfix( continue; } - let current_package_id = current_package_id.get_or_insert(package_id.clone()); + let current_target = current_target.get_or_insert(target.clone()); - if current_package_id == &package_id { + if current_target == &target { file_map .entry(file_name) .or_insert_with(IndexSet::new) diff --git a/src/check.rs b/src/check.rs index a16c13f..70c02db 100644 --- a/src/check.rs +++ b/src/check.rs @@ -1,160 +1,53 @@ -use clap::Parser; - -#[derive(Debug, Parser)] -pub struct CheckFlags { - /// Package(s) to fix - #[arg(long, value_name = "SPEC", help_heading = "Package Selection")] - package: Vec, - - /// Fix all packages in the workspace - #[arg(long, help_heading = "Package Selection")] - workspace: bool, - - /// Exclude packages from the fixes - #[arg(long, value_name = "SPEC", help_heading = "Package Selection")] - exclude: Vec, - - /// Alias for --workspace (deprecated) - #[arg(long, help_heading = "Package Selection")] - all: bool, - - /// Fix only this package's library - #[arg(long, help_heading = "Target Selection")] - lib: bool, - - /// Fix all binaries - #[arg(long, help_heading = "Target Selection")] - bins: bool, - - /// Fix only the specified binary - #[arg(long, value_name = "NAME", help_heading = "Target Selection")] - bin: Option, - - /// Fix all examples - #[arg(long, help_heading = "Target Selection")] - examples: bool, - - /// Fix only the specified binary - #[arg(long, value_name = "NAME", help_heading = "Target Selection")] - example: Option, - - /// Fix all tests - #[arg(long, help_heading = "Target Selection")] - tests: bool, - - /// Fix only the specified test - #[arg(long, value_name = "NAME", help_heading = "Target Selection")] - test: Option, - - /// Fix all benches - #[arg(long, help_heading = "Target Selection")] - benches: bool, - - /// Fix only the specified bench - #[arg(long, value_name = "NAME", help_heading = "Target Selection")] - bench: Option, - - /// Fix all targets - #[arg(long, help_heading = "Target Selection")] - all_targets: bool, - - /// Space or comma separated list of features to activate - #[arg( - short = 'F', - long, - value_name = "FEATURES", - help_heading = "Feature Selection" - )] - features: Vec, - - /// Activate all available features - #[arg(long, help_heading = "Feature Selection")] - all_features: bool, - - /// Do not activate the `default` feature - #[arg(long, help_heading = "Feature Selection")] - no_default_features: bool, - - /// Unstable (nightly-only) flags - #[arg(short = 'Z', value_name = "FLAG")] - unstable_flags: Vec, +use rustfix::diagnostics::Diagnostic; +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct CheckMessage { + pub target: Target, + pub message: Diagnostic, + pub package_id: String, } -impl CheckFlags { - pub fn to_flags(&self) -> Vec { - let mut out = Vec::new(); - - for spec in self.package.clone() { - out.push("--package".to_owned()); - out.push(spec); - } - if self.workspace { - out.push("--workspace".to_owned()); - } - for spec in self.exclude.clone() { - out.push("--exclude".to_owned()); - out.push(spec); - } - if self.all { - out.push("--all".to_owned()); - } - - if self.lib { - out.push("--lib".to_owned()); - } - - if self.bins { - out.push("--bins".to_owned()); - } - if let Some(b) = self.bin.clone() { - out.push("--bin".to_owned()); - out.push(b); - } - - if self.examples { - out.push("--examples".to_owned()); - } - if let Some(b) = self.example.clone() { - out.push("--example".to_owned()); - out.push(b); - } - - if self.tests { - out.push("--tests".to_owned()); - } - if let Some(b) = self.test.clone() { - out.push("--test".to_owned()); - out.push(b); - } - - if self.benches { - out.push("--benches".to_owned()); - } - if let Some(b) = self.bench.clone() { - out.push("--bench".to_owned()); - out.push(b); - } - - if self.all_targets { - out.push("--all-targets".to_owned()); - } - - for i in self.features.clone() { - out.push("--features".to_owned()); - out.push(i); - } - if self.all_features { - out.push("--all-features".to_owned()); - } - if self.no_default_features { - out.push("--no-default-features".to_owned()); - } +#[derive(Deserialize, Hash, PartialEq, Clone, Eq)] +pub struct Target { + kind: Vec, + crate_types: Vec, + name: String, + src_path: String, + edition: String, + doc: bool, + doctest: bool, + test: bool, +} - for i in self.unstable_flags.clone() { - out.push("-Z".to_owned()); - out.push(i); - } +#[derive(Deserialize, Hash, PartialEq, Clone, Eq)] +#[serde(rename_all(deserialize = "kebab-case"))] +pub enum Kind { + Bin, + Example, + Test, + Bench, + CustomBuild, + Lib, + Rlib, + Dylib, + Cdylib, + Staticlib, + ProcMacro, + #[serde(untagged)] + Other(String), +} - out - } +#[derive(Deserialize, Hash, PartialEq, Clone, Eq)] +#[serde(rename_all(deserialize = "kebab-case"))] +pub enum CrateType { + Bin, + Lib, + Rlib, + Dylib, + Cdylib, + Staticlib, + ProcMacro, + #[serde(untagged)] + Other(String), } diff --git a/src/flags.rs b/src/flags.rs new file mode 100644 index 0000000..a16c13f --- /dev/null +++ b/src/flags.rs @@ -0,0 +1,160 @@ +use clap::Parser; + +#[derive(Debug, Parser)] +pub struct CheckFlags { + /// Package(s) to fix + #[arg(long, value_name = "SPEC", help_heading = "Package Selection")] + package: Vec, + + /// Fix all packages in the workspace + #[arg(long, help_heading = "Package Selection")] + workspace: bool, + + /// Exclude packages from the fixes + #[arg(long, value_name = "SPEC", help_heading = "Package Selection")] + exclude: Vec, + + /// Alias for --workspace (deprecated) + #[arg(long, help_heading = "Package Selection")] + all: bool, + + /// Fix only this package's library + #[arg(long, help_heading = "Target Selection")] + lib: bool, + + /// Fix all binaries + #[arg(long, help_heading = "Target Selection")] + bins: bool, + + /// Fix only the specified binary + #[arg(long, value_name = "NAME", help_heading = "Target Selection")] + bin: Option, + + /// Fix all examples + #[arg(long, help_heading = "Target Selection")] + examples: bool, + + /// Fix only the specified binary + #[arg(long, value_name = "NAME", help_heading = "Target Selection")] + example: Option, + + /// Fix all tests + #[arg(long, help_heading = "Target Selection")] + tests: bool, + + /// Fix only the specified test + #[arg(long, value_name = "NAME", help_heading = "Target Selection")] + test: Option, + + /// Fix all benches + #[arg(long, help_heading = "Target Selection")] + benches: bool, + + /// Fix only the specified bench + #[arg(long, value_name = "NAME", help_heading = "Target Selection")] + bench: Option, + + /// Fix all targets + #[arg(long, help_heading = "Target Selection")] + all_targets: bool, + + /// Space or comma separated list of features to activate + #[arg( + short = 'F', + long, + value_name = "FEATURES", + help_heading = "Feature Selection" + )] + features: Vec, + + /// Activate all available features + #[arg(long, help_heading = "Feature Selection")] + all_features: bool, + + /// Do not activate the `default` feature + #[arg(long, help_heading = "Feature Selection")] + no_default_features: bool, + + /// Unstable (nightly-only) flags + #[arg(short = 'Z', value_name = "FLAG")] + unstable_flags: Vec, +} + +impl CheckFlags { + pub fn to_flags(&self) -> Vec { + let mut out = Vec::new(); + + for spec in self.package.clone() { + out.push("--package".to_owned()); + out.push(spec); + } + if self.workspace { + out.push("--workspace".to_owned()); + } + for spec in self.exclude.clone() { + out.push("--exclude".to_owned()); + out.push(spec); + } + if self.all { + out.push("--all".to_owned()); + } + + if self.lib { + out.push("--lib".to_owned()); + } + + if self.bins { + out.push("--bins".to_owned()); + } + if let Some(b) = self.bin.clone() { + out.push("--bin".to_owned()); + out.push(b); + } + + if self.examples { + out.push("--examples".to_owned()); + } + if let Some(b) = self.example.clone() { + out.push("--example".to_owned()); + out.push(b); + } + + if self.tests { + out.push("--tests".to_owned()); + } + if let Some(b) = self.test.clone() { + out.push("--test".to_owned()); + out.push(b); + } + + if self.benches { + out.push("--benches".to_owned()); + } + if let Some(b) = self.bench.clone() { + out.push("--bench".to_owned()); + out.push(b); + } + + if self.all_targets { + out.push("--all-targets".to_owned()); + } + + for i in self.features.clone() { + out.push("--features".to_owned()); + out.push(i); + } + if self.all_features { + out.push("--all-features".to_owned()); + } + if self.no_default_features { + out.push("--no-default-features".to_owned()); + } + + for i in self.unstable_flags.clone() { + out.push("-Z".to_owned()); + out.push(i); + } + + out + } +} diff --git a/src/lib.rs b/src/lib.rs index 244b29c..e55bd86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ mod check; mod errors; +mod flags; pub mod shell; -pub use check::CheckFlags; +pub use check::*; pub use errors::*; +pub use flags::CheckFlags; diff --git a/tests/testsuite/fixit.rs b/tests/testsuite/fixit.rs index 2cc8d06..e51ab16 100644 --- a/tests/testsuite/fixit.rs +++ b/tests/testsuite/fixit.rs @@ -143,3 +143,23 @@ fn dependency_order() { "#]]) .run(); } + +#[cargo_test] +fn build_unit_order() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("build.rs", "fn main(){ let mut a = 1; let _ = a; }") + .file("src/lib.rs", "fn _a(){ let mut a = 1; let _ = a; }") + .file("src/main.rs", "fn main(){ let mut a = 1; let _ = a; }") + .build(); + + p.cargo_("fixit --allow-no-vcs") + .with_status(0) + .with_stderr_data(str![[r#" +[FIXED] build.rs (1 fix) +[FIXED] src/lib.rs (1 fix) +[FIXED] src/main.rs (1 fix) + +"#]]) + .run(); +}