diff --git a/compiler/rustc_driver_impl/src/args.rs b/compiler/rustc_driver_impl/src/args.rs index b0970144c421e..2f023b059377c 100644 --- a/compiler/rustc_driver_impl/src/args.rs +++ b/compiler/rustc_driver_impl/src/args.rs @@ -1,6 +1,6 @@ use std::{env, error, fmt, fs, io}; -use rustc_session::EarlyDiagCtxt; +use rustc_session::{EarlyDiagCtxt, config}; /// Expands argfiles in command line arguments. #[derive(Default)] @@ -153,3 +153,40 @@ impl fmt::Display for Error { } impl error::Error for Error {} + +/// check if suspect is a confusables of any option or arg in rustc or fake_args +pub(crate) fn check_confusables(suspect: &str, fake_args: &[&str]) -> Option { + let suspect = suspect.strip_prefix("-").unwrap_or(suspect); + // Check if s1 starts with s2, ignoring hyphens vs underscores + fn starts_with_ignoring_separators(s1: &str, s2: &str) -> bool { + let normalized_s1 = s1.replace('-', "_"); + let normalized_s2 = s2.replace('-', "_"); + normalized_s1.starts_with(&normalized_s2) + } + + let optgroups = config::rustc_optgroups(); + for option in optgroups { + let prefix = String::from("--"); + if starts_with_ignoring_separators(option.long_name(), suspect) { + return Some(prefix + option.long_name()); + } + } + for option in config::CG_OPTIONS { + let prefix = String::from("-C "); + if starts_with_ignoring_separators(option.name(), suspect) { + return Some(prefix + option.name()); + } + } + for option in config::Z_OPTIONS { + let prefix = String::from("-Z "); + if starts_with_ignoring_separators(option.name(), suspect) { + return Some(prefix + option.name()); + } + } + for arg in fake_args { + if starts_with_ignoring_separators(arg, suspect) { + return Some("".to_string()); + } + } + None +} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 1849038545556..d0cc98d75dab3 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -103,6 +103,7 @@ mod signal_handler { pub(super) fn install() {} } +use crate::args::check_confusables; use crate::session_diagnostics::{ CantEmitMIR, RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch, RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, UnstableFeatureUsage, @@ -1237,6 +1238,26 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option options.optflagmulti(short_name, long_name, desc), }; } + + pub fn long_name(&self) -> &str { + self.long_name + } } pub fn make_opt( diff --git a/tests/run-make/option-output-no-space/main.rs b/tests/run-make/option-output-no-space/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make/option-output-no-space/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/option-output-no-space/rmake.rs b/tests/run-make/option-output-no-space/rmake.rs new file mode 100644 index 0000000000000..69b5ace250ebb --- /dev/null +++ b/tests/run-make/option-output-no-space/rmake.rs @@ -0,0 +1,82 @@ +// This test is to check if the warning is emitted when no space +// between `-o` and arg is applied, see issue #142812 +use run_make_support::rustc; + +fn main() { + // test fake args + rustc() + .input("main.rs") + .arg("-optimize") + .run() + .assert_stderr_contains( + "warning: option `-o` has no space between flag name and value, which can be confusing", + ) + .assert_stderr_contains( + "note: option `-o ptimize` is applied instead of a flag named `optimize`", + ) + .assert_stderr_contains("to specify output filename `ptimize`"); + rustc() + .input("main.rs") + .arg("-o0") + .run() + .assert_stderr_contains( + "warning: option `-o` has no space between flag name and value, which can be confusing", + ) + .assert_stderr_contains("note: option `-o 0` is applied instead of a flag named `o0`") + .assert_stderr_contains("to specify output filename `0`"); + rustc().input("main.rs").arg("-o1").run(); + // test real args by iter optgroups + rustc() + .input("main.rs") + .arg("-out-dir") + .run() + .assert_stderr_contains( + "warning: option `-o` has no space between flag name and value, which can be confusing", + ) + .assert_stderr_contains( + "note: option `-o ut-dir` is applied instead of a flag named `out-dir`", + ) + .assert_stderr_contains("to specify output filename `ut-dir`") + .assert_stderr_contains("Do you mean `--out-dir`?"); + // test real args by iter CG_OPTIONS + rustc() + .input("main.rs") + .arg("-opt-level") + .run() + .assert_stderr_contains( + "warning: option `-o` has no space between flag name and value, which can be confusing", + ) + .assert_stderr_contains( + "note: option `-o pt-level` is applied instead of a flag named `opt-level`", + ) + .assert_stderr_contains("to specify output filename `pt-level`") + .assert_stderr_contains("Do you mean `-C opt_level`?"); + rustc() + .input("main.rs") + .arg("-overflow-checks") + .run() + .assert_stderr_contains( + "warning: option `-o` has no space between flag name and value, which can be confusing", + ) + .assert_stderr_contains( + "note: option `-o verflow-checks` is applied instead of a flag named `overflow-checks`", + ) + .assert_stderr_contains("to specify output filename `verflow-checks`") + .assert_stderr_contains("Do you mean `-C overflow_checks`?"); + // test real args by iter Z_OPTIONS + rustc() + .input("main.rs") + .arg("-oom") + .run() + .assert_stderr_contains( + "warning: option `-o` has no space between flag name and value, which can be confusing", + ) + .assert_stderr_contains("note: option `-o om` is applied instead of a flag named `oom`") + .assert_stderr_contains("to specify output filename `om`") + .assert_stderr_contains("note: Do you mean `-Z oom`?"); + + // test no warning when there is space between `-o` and arg + rustc().input("main.rs").arg("-o").arg("ptimize").run().assert_stderr_equals(""); + rustc().input("main.rs").arg("--out-dir").arg("xxx").run().assert_stderr_equals(""); + rustc().input("main.rs").arg("-o").arg("out-dir").run().assert_stderr_equals(""); +}