From 80f9d318d5b7a58575e24c08d72ce010aafeb223 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 31 Jul 2018 15:42:38 -0700 Subject: [PATCH] Add a `--edition-idioms` flag to `cargo fix` This, like `--prepare-for`, will be part of the transition guide which automatically applies the necessary lint group from the compiler to associated code. The `--edition-idioms` flag does not take an argument and will automatically enable the right lint group based on the edition being compiled for. cc #52679 --- src/bin/cargo/commands/fix.rs | 6 ++++ src/cargo/ops/fix.rs | 23 ++++++++++++ src/cargo/util/diagnostic_server.rs | 33 ++++++++++++++++- tests/testsuite/fix.rs | 56 +++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) diff --git a/src/bin/cargo/commands/fix.rs b/src/bin/cargo/commands/fix.rs index 976a383121b..4cbf9b9565e 100644 --- a/src/bin/cargo/commands/fix.rs +++ b/src/bin/cargo/commands/fix.rs @@ -51,6 +51,11 @@ pub fn cli() -> App { .conflicts_with("edition") .hidden(true), ) + .arg( + Arg::with_name("idioms") + .long("edition-idioms") + .help("Fix warnings to migrate to the idioms of an edition") + ) .arg( Arg::with_name("allow-no-vcs") .long("allow-no-vcs") @@ -126,6 +131,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { ops::fix(&ws, &mut ops::FixOptions { edition: args.is_present("edition"), prepare_for: args.value_of("prepare-for"), + idioms: args.is_present("idioms"), compile_opts: opts, allow_dirty: args.is_present("allow-dirty"), allow_no_vcs: args.is_present("allow-no-vcs"), diff --git a/src/cargo/ops/fix.rs b/src/cargo/ops/fix.rs index dbe3c926fd4..662c573c215 100644 --- a/src/cargo/ops/fix.rs +++ b/src/cargo/ops/fix.rs @@ -24,9 +24,12 @@ const BROKEN_CODE_ENV: &str = "__CARGO_FIX_BROKEN_CODE"; const PREPARE_FOR_ENV: &str = "__CARGO_FIX_PREPARE_FOR"; const EDITION_ENV: &str = "__CARGO_FIX_EDITION"; +const IDIOMS_ENV: &str = "__CARGO_FIX_IDIOMS"; + pub struct FixOptions<'a> { pub edition: bool, pub prepare_for: Option<&'a str>, + pub idioms: bool, pub compile_opts: CompileOptions<'a>, pub allow_dirty: bool, pub allow_no_vcs: bool, @@ -59,6 +62,12 @@ pub fn fix(ws: &Workspace, opts: &mut FixOptions) -> CargoResult<()> { edition.to_string(), )); } + if opts.idioms { + opts.compile_opts.build_config.extra_rustc_env.push(( + IDIOMS_ENV.to_string(), + "1".to_string(), + )); + } opts.compile_opts.build_config.cargo_as_rustc_wrapper = true; *opts.compile_opts.build_config.rustfix_diagnostic_server.borrow_mut() = Some(RustfixDiagnosticServer::new()?); @@ -471,6 +480,7 @@ fn log_failed_fix(stderr: &[u8]) -> Result<(), Error> { struct FixArgs { file: Option, prepare_for_edition: PrepareFor, + idioms: bool, enabled_edition: Option, other: Vec, } @@ -512,6 +522,7 @@ impl FixArgs { } else if env::var(EDITION_ENV).is_ok() { ret.prepare_for_edition = PrepareFor::Next; } + ret.idioms = env::var(IDIOMS_ENV).is_ok(); return ret } @@ -523,6 +534,12 @@ impl FixArgs { .arg("--cap-lints=warn"); if let Some(edition) = &self.enabled_edition { cmd.arg("--edition").arg(edition); + if self.idioms { + match &edition[..] { + "2018" => { cmd.arg("-Wrust-2018-idioms"); } + _ => {} + } + } } match &self.prepare_for_edition { PrepareFor::Edition(edition) => { @@ -569,6 +586,12 @@ impl FixArgs { process::exit(1); } + /// If we're preparing for an edition and we *don't* find the + /// `rust_2018_preview` feature, for example, in the entry point file then + /// it probably means that the edition isn't actually enabled, so we can't + /// actually fix anything. + /// + /// If this is the case, issue a warning. fn warn_if_preparing_probably_inert(&self) -> CargoResult<()> { let edition = match &self.prepare_for_edition { PrepareFor::Edition(s) => s, diff --git a/src/cargo/util/diagnostic_server.rs b/src/cargo/util/diagnostic_server.rs index f5018753756..4e86d99e608 100644 --- a/src/cargo/util/diagnostic_server.rs +++ b/src/cargo/util/diagnostic_server.rs @@ -48,6 +48,11 @@ pub enum Message { file: String, edition: String, }, + IdiomEditionMismatch { + file: String, + idioms: String, + edition: Option, + }, } impl Message { @@ -78,6 +83,7 @@ pub struct DiagnosticPrinter<'a> { config: &'a Config, preview_not_found: HashSet, edition_already_enabled: HashSet, + idiom_mismatch: HashSet, } impl<'a> DiagnosticPrinter<'a> { @@ -86,6 +92,7 @@ impl<'a> DiagnosticPrinter<'a> { config, preview_not_found: HashSet::new(), edition_already_enabled: HashSet::new(), + idiom_mismatch: HashSet::new(), } } @@ -172,8 +179,32 @@ information about transitioning to the {0} edition see: self.config.shell().error(&msg)?; Ok(()) } - } + Message::IdiomEditionMismatch { file, idioms, edition } => { + // Same as above + if !self.idiom_mismatch.insert(file.clone()) { + return Ok(()) + } + self.config.shell().error(&format!( + "\ +cannot migrate to the idioms of the {} edition for `{}` +because it is compiled {}, which doesn't match {0} + +consider migrating to the {0} edition by adding `edition = '{0}'` to +`Cargo.toml` and then rerunning this command; a more detailed transition +guide can be found at + https://rust-lang-nursery.github.io/edition-guide/editions/transitioning.html +", + idioms, + file, + match edition { + Some(s) => format!("with the {} edition", s), + None => format!("without an edition"), + }, + ))?; + Ok(()) + } + } } } diff --git a/tests/testsuite/fix.rs b/tests/testsuite/fix.rs index a24ccc05047..d26fc32dc5b 100644 --- a/tests/testsuite/fix.rs +++ b/tests/testsuite/fix.rs @@ -966,6 +966,62 @@ fn fix_overlapping() { assert!(contents.contains("crate::foo::()")); } +#[test] +fn fix_idioms() { + if !is_nightly() { + return + } + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ['edition'] + [package] + name = 'foo' + version = '0.1.0' + edition = '2018' + "#, + ) + .file( + "src/lib.rs", + r#" + use std::any::Any; + pub fn foo() { + let _x: Box = Box::new(3); + } + "# + ) + .build(); + + let stderr = "\ +[CHECKING] foo [..] +[FIXING] src/lib.rs (1 fix) +[FINISHED] [..] +"; + assert_that( + p.cargo("fix --edition-idioms --allow-no-vcs") + .masquerade_as_nightly_cargo(), + execs() + .with_stderr(stderr) + .with_status(0), + ); + + assert!(p.read_file("src/lib.rs").contains("Box")); +} + +#[test] +fn idioms_2015_ok() { + let p = project() + .file("src/lib.rs", "") + .build(); + + assert_that( + p.cargo("fix --edition-idioms --allow-no-vcs") + .masquerade_as_nightly_cargo(), + execs().with_status(0), + ); +} + #[test] fn both_edition_migrate_flags() { if !is_nightly() {