Skip to content

Commit 9f7bd62

Browse files
committed
Auto merge of #7069 - yaahallo:master, r=ehuss
initial working version of cargo fix --clippy closes #7006
2 parents 56fca89 + 22b430d commit 9f7bd62

File tree

12 files changed

+210
-63
lines changed

12 files changed

+210
-63
lines changed

src/bin/cargo/commands/clippy.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
6969
.into());
7070
}
7171

72-
let wrapper = util::process("clippy-driver");
73-
compile_opts.build_config.rustc_wrapper = Some(wrapper);
72+
let wrapper = util::process(util::config::clippy_driver());
73+
compile_opts.build_config.primary_unit_rustc = Some(wrapper);
7474

7575
ops::compile(&ws, &compile_opts)?;
7676
Ok(())

src/bin/cargo/commands/fix.rs

+26
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ pub fn cli() -> App {
7272
.long("allow-staged")
7373
.help("Fix code even if the working directory has staged changes"),
7474
)
75+
.arg(
76+
Arg::with_name("clippy")
77+
.long("clippy")
78+
.help("Get fix suggestions from clippy instead of rustc")
79+
.hidden(true)
80+
.multiple(true)
81+
.min_values(0)
82+
.number_of_values(1),
83+
)
7584
.after_help(
7685
"\
7786
This Cargo subcommand will automatically take rustc's suggestions from
@@ -125,6 +134,21 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
125134
// code as we can.
126135
let mut opts = args.compile_options(config, mode, Some(&ws))?;
127136

137+
let use_clippy = args.is_present("clippy");
138+
139+
let clippy_args = args
140+
.value_of("clippy")
141+
.map(|s| s.split(' ').map(|s| s.to_string()).collect())
142+
.or_else(|| Some(vec![]))
143+
.filter(|_| use_clippy);
144+
145+
if use_clippy && !config.cli_unstable().unstable_options {
146+
return Err(failure::format_err!(
147+
"`cargo fix --clippy` is unstable, pass `-Z unstable-options` to enable it"
148+
)
149+
.into());
150+
}
151+
128152
if let CompileFilter::Default { .. } = opts.filter {
129153
opts.filter = CompileFilter::Only {
130154
all_targets: true,
@@ -135,6 +159,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
135159
tests: FilterRule::All,
136160
}
137161
}
162+
138163
ops::fix(
139164
&ws,
140165
&mut ops::FixOptions {
@@ -146,6 +171,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
146171
allow_no_vcs: args.is_present("allow-no-vcs"),
147172
allow_staged: args.is_present("allow-staged"),
148173
broken_code: args.is_present("broken-code"),
174+
clippy_args,
149175
},
150176
)?;
151177
Ok(())

src/cargo/core/compiler/build_config.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ pub struct BuildConfig {
2424
pub force_rebuild: bool,
2525
/// Output a build plan to stdout instead of actually compiling.
2626
pub build_plan: bool,
27-
/// An optional wrapper, if any, used to wrap rustc invocations
28-
pub rustc_wrapper: Option<ProcessBuilder>,
27+
/// An optional override of the rustc path for primary units only
28+
pub primary_unit_rustc: Option<ProcessBuilder>,
2929
pub rustfix_diagnostic_server: RefCell<Option<RustfixDiagnosticServer>>,
3030
/// Whether or not Cargo should cache compiler output on disk.
3131
cache_messages: bool,
@@ -98,7 +98,7 @@ impl BuildConfig {
9898
message_format: MessageFormat::Human,
9999
force_rebuild: false,
100100
build_plan: false,
101-
rustc_wrapper: None,
101+
primary_unit_rustc: None,
102102
rustfix_diagnostic_server: RefCell::new(None),
103103
cache_messages: config.cli_unstable().cache_messages,
104104
})

src/cargo/core/compiler/build_context/mod.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,7 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> {
5151
units: &'a UnitInterner<'a>,
5252
extra_compiler_args: HashMap<Unit<'a>, Vec<String>>,
5353
) -> CargoResult<BuildContext<'a, 'cfg>> {
54-
let mut rustc = config.load_global_rustc(Some(ws))?;
55-
if let Some(wrapper) = &build_config.rustc_wrapper {
56-
rustc.set_wrapper(wrapper.clone());
57-
}
54+
let rustc = config.load_global_rustc(Some(ws))?;
5855

5956
let host_config = TargetConfig::new(config, &rustc.host)?;
6057
let target_config = match build_config.requested_target.as_ref() {

src/cargo/core/compiler/compilation.rs

+28-6
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ pub struct Compilation<'cfg> {
6969

7070
config: &'cfg Config,
7171
rustc_process: ProcessBuilder,
72+
primary_unit_rustc_process: Option<ProcessBuilder>,
7273

7374
target_runner: Option<(PathBuf, Vec<String>)>,
7475
}
@@ -77,13 +78,20 @@ impl<'cfg> Compilation<'cfg> {
7778
pub fn new<'a>(bcx: &BuildContext<'a, 'cfg>) -> CargoResult<Compilation<'cfg>> {
7879
let mut rustc = bcx.rustc.process();
7980

81+
let mut primary_unit_rustc_process =
82+
bcx.build_config.primary_unit_rustc.clone().map(|mut r| {
83+
r.arg(&bcx.rustc.path);
84+
r
85+
});
86+
8087
if bcx.config.extra_verbose() {
8188
rustc.display_env_vars();
89+
90+
if let Some(rustc) = primary_unit_rustc_process.as_mut() {
91+
rustc.display_env_vars();
92+
}
8293
}
83-
let srv = bcx.build_config.rustfix_diagnostic_server.borrow();
84-
if let Some(server) = &*srv {
85-
server.configure(&mut rustc);
86-
}
94+
8795
Ok(Compilation {
8896
// TODO: deprecated; remove.
8997
native_dirs: BTreeSet::new(),
@@ -100,15 +108,29 @@ impl<'cfg> Compilation<'cfg> {
100108
rustdocflags: HashMap::new(),
101109
config: bcx.config,
102110
rustc_process: rustc,
111+
primary_unit_rustc_process,
103112
host: bcx.host_triple().to_string(),
104113
target: bcx.target_triple().to_string(),
105114
target_runner: target_runner(bcx)?,
106115
})
107116
}
108117

109118
/// See `process`.
110-
pub fn rustc_process(&self, pkg: &Package, target: &Target) -> CargoResult<ProcessBuilder> {
111-
let mut p = self.fill_env(self.rustc_process.clone(), pkg, true)?;
119+
pub fn rustc_process(
120+
&self,
121+
pkg: &Package,
122+
target: &Target,
123+
is_primary: bool,
124+
) -> CargoResult<ProcessBuilder> {
125+
let rustc = if is_primary {
126+
self.primary_unit_rustc_process
127+
.clone()
128+
.unwrap_or_else(|| self.rustc_process.clone())
129+
} else {
130+
self.rustc_process.clone()
131+
};
132+
133+
let mut p = self.fill_env(rustc, pkg, true)?;
112134
if target.edition() != Edition::Edition2015 {
113135
p.arg(format!("--edition={}", target.edition()));
114136
}

src/cargo/core/compiler/mod.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,6 @@ fn rustc<'a, 'cfg>(
180180
exec: &Arc<dyn Executor>,
181181
) -> CargoResult<Work> {
182182
let mut rustc = prepare_rustc(cx, &unit.target.rustc_crate_types(), unit)?;
183-
if cx.is_primary_package(unit) {
184-
rustc.env("CARGO_PRIMARY_PACKAGE", "1");
185-
}
186183
let build_plan = cx.bcx.build_config.build_plan;
187184

188185
let name = unit.pkg.name().to_string();
@@ -593,7 +590,11 @@ fn prepare_rustc<'a, 'cfg>(
593590
crate_types: &[&str],
594591
unit: &Unit<'a>,
595592
) -> CargoResult<ProcessBuilder> {
596-
let mut base = cx.compilation.rustc_process(unit.pkg, unit.target)?;
593+
let is_primary = cx.is_primary_package(unit);
594+
595+
let mut base = cx
596+
.compilation
597+
.rustc_process(unit.pkg, unit.target, is_primary)?;
597598
base.inherit_jobserver(&cx.jobserver);
598599
build_base_args(cx, &mut base, unit, crate_types)?;
599600
build_deps_args(&mut base, cx, unit)?;

src/cargo/ops/fix.rs

+50-20
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const BROKEN_CODE_ENV: &str = "__CARGO_FIX_BROKEN_CODE";
6363
const PREPARE_FOR_ENV: &str = "__CARGO_FIX_PREPARE_FOR";
6464
const EDITION_ENV: &str = "__CARGO_FIX_EDITION";
6565
const IDIOMS_ENV: &str = "__CARGO_FIX_IDIOMS";
66+
const CLIPPY_FIX_ARGS: &str = "__CARGO_FIX_CLIPPY_ARGS";
6667

6768
pub struct FixOptions<'a> {
6869
pub edition: bool,
@@ -73,6 +74,7 @@ pub struct FixOptions<'a> {
7374
pub allow_no_vcs: bool,
7475
pub allow_staged: bool,
7576
pub broken_code: bool,
77+
pub clippy_args: Option<Vec<String>>,
7678
}
7779

7880
pub fn fix(ws: &Workspace<'_>, opts: &mut FixOptions<'_>) -> CargoResult<()> {
@@ -99,12 +101,38 @@ pub fn fix(ws: &Workspace<'_>, opts: &mut FixOptions<'_>) -> CargoResult<()> {
99101
wrapper.env(IDIOMS_ENV, "1");
100102
}
101103

104+
if opts.clippy_args.is_some() {
105+
if let Err(e) = util::process("clippy-driver").arg("-V").exec_with_output() {
106+
eprintln!("Warning: clippy-driver not found: {:?}", e);
107+
}
108+
109+
let clippy_args = opts
110+
.clippy_args
111+
.as_ref()
112+
.map_or_else(String::new, |args| serde_json::to_string(&args).unwrap());
113+
114+
wrapper.env(CLIPPY_FIX_ARGS, clippy_args);
115+
}
116+
102117
*opts
103118
.compile_opts
104119
.build_config
105120
.rustfix_diagnostic_server
106121
.borrow_mut() = Some(RustfixDiagnosticServer::new()?);
107-
opts.compile_opts.build_config.rustc_wrapper = Some(wrapper);
122+
123+
if let Some(server) = opts
124+
.compile_opts
125+
.build_config
126+
.rustfix_diagnostic_server
127+
.borrow()
128+
.as_ref()
129+
{
130+
server.configure(&mut wrapper);
131+
}
132+
133+
// primary crates are compiled using a cargo subprocess to do extra work of applying fixes and
134+
// repeating build until there are no more changes to be applied
135+
opts.compile_opts.build_config.primary_unit_rustc = Some(wrapper);
108136

109137
ops::compile(ws, &opts.compile_opts)?;
110138
Ok(())
@@ -193,18 +221,10 @@ pub fn fix_maybe_exec_rustc() -> CargoResult<bool> {
193221
trace!("cargo-fix as rustc got file {:?}", args.file);
194222
let rustc = args.rustc.as_ref().expect("fix wrapper rustc was not set");
195223

196-
// Our goal is to fix only the crates that the end user is interested in.
197-
// That's very likely to only mean the crates in the workspace the user is
198-
// working on, not random crates.io crates.
199-
//
200-
// The master cargo process tells us whether or not this is a "primary"
201-
// crate via the CARGO_PRIMARY_PACKAGE environment variable.
202224
let mut fixes = FixedCrate::default();
203225
if let Some(path) = &args.file {
204-
if args.primary_package {
205-
trace!("start rustfixing {:?}", path);
206-
fixes = rustfix_crate(&lock_addr, rustc.as_ref(), path, &args)?;
207-
}
226+
trace!("start rustfixing {:?}", path);
227+
fixes = rustfix_crate(&lock_addr, rustc.as_ref(), path, &args)?;
208228
}
209229

210230
// Ok now we have our final goal of testing out the changes that we applied.
@@ -253,7 +273,6 @@ pub fn fix_maybe_exec_rustc() -> CargoResult<bool> {
253273
}
254274

255275
// This final fall-through handles multiple cases;
256-
// - Non-primary crates, which need to be built.
257276
// - If the fix failed, show the original warnings and suggestions.
258277
// - If `--broken-code`, show the error messages.
259278
// - If the fix succeeded, show any remaining warnings.
@@ -563,8 +582,8 @@ struct FixArgs {
563582
idioms: bool,
564583
enabled_edition: Option<String>,
565584
other: Vec<OsString>,
566-
primary_package: bool,
567585
rustc: Option<PathBuf>,
586+
clippy_args: Vec<String>,
568587
}
569588

570589
enum PrepareFor {
@@ -582,7 +601,14 @@ impl Default for PrepareFor {
582601
impl FixArgs {
583602
fn get() -> FixArgs {
584603
let mut ret = FixArgs::default();
585-
ret.rustc = env::args_os().nth(1).map(PathBuf::from);
604+
605+
if let Ok(clippy_args) = env::var(CLIPPY_FIX_ARGS) {
606+
ret.clippy_args = serde_json::from_str(&clippy_args).unwrap();
607+
ret.rustc = Some(util::config::clippy_driver());
608+
} else {
609+
ret.rustc = env::args_os().nth(1).map(PathBuf::from);
610+
}
611+
586612
for arg in env::args_os().skip(2) {
587613
let path = PathBuf::from(arg);
588614
if path.extension().and_then(|s| s.to_str()) == Some("rs") && path.exists() {
@@ -608,26 +634,30 @@ impl FixArgs {
608634
} else if env::var(EDITION_ENV).is_ok() {
609635
ret.prepare_for_edition = PrepareFor::Next;
610636
}
637+
611638
ret.idioms = env::var(IDIOMS_ENV).is_ok();
612-
ret.primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok();
613639
ret
614640
}
615641

616642
fn apply(&self, cmd: &mut Command) {
617643
if let Some(path) = &self.file {
618644
cmd.arg(path);
619645
}
646+
647+
if !self.clippy_args.is_empty() {
648+
cmd.args(&self.clippy_args);
649+
}
650+
620651
cmd.args(&self.other).arg("--cap-lints=warn");
621652
if let Some(edition) = &self.enabled_edition {
622653
cmd.arg("--edition").arg(edition);
623-
if self.idioms && self.primary_package && edition == "2018" {
654+
if self.idioms && edition == "2018" {
624655
cmd.arg("-Wrust-2018-idioms");
625656
}
626657
}
627-
if self.primary_package {
628-
if let Some(edition) = self.prepare_for_edition_resolve() {
629-
cmd.arg("-W").arg(format!("rust-{}-compatibility", edition));
630-
}
658+
659+
if let Some(edition) = self.prepare_for_edition_resolve() {
660+
cmd.arg("-W").arg(format!("rust-{}-compatibility", edition));
631661
}
632662
}
633663

src/cargo/util/config.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1782,3 +1782,12 @@ impl Drop for PackageCacheLock<'_> {
17821782
}
17831783
}
17841784
}
1785+
1786+
/// returns path to clippy-driver binary
1787+
///
1788+
/// Allows override of the path via `CARGO_CLIPPY_DRIVER` env variable
1789+
pub fn clippy_driver() -> PathBuf {
1790+
env::var("CARGO_CLIPPY_DRIVER")
1791+
.unwrap_or_else(|_| "clippy-driver".into())
1792+
.into()
1793+
}

src/cargo/util/rustc.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,22 @@ impl Rustc {
6767
}
6868

6969
/// Gets a process builder set up to use the found rustc version, with a wrapper if `Some`.
70-
pub fn process(&self) -> ProcessBuilder {
70+
pub fn process_with(&self, path: impl AsRef<Path>) -> ProcessBuilder {
7171
match self.wrapper {
7272
Some(ref wrapper) if !wrapper.get_program().is_empty() => {
7373
let mut cmd = wrapper.clone();
74-
cmd.arg(&self.path);
74+
cmd.arg(path.as_ref());
7575
cmd
7676
}
77-
_ => self.process_no_wrapper(),
77+
_ => util::process(path.as_ref()),
7878
}
7979
}
8080

81+
/// Gets a process builder set up to use the found rustc version, with a wrapper if `Some`.
82+
pub fn process(&self) -> ProcessBuilder {
83+
self.process_with(&self.path)
84+
}
85+
8186
pub fn process_no_wrapper(&self) -> ProcessBuilder {
8287
util::process(&self.path)
8388
}

0 commit comments

Comments
 (0)