about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorMatthias Krüger <476013+matthiaskrgr@users.noreply.github.com>2025-07-18 04:27:51 +0200
committerGitHub <noreply@github.com>2025-07-18 04:27:51 +0200
commit26f5936cf9f2d08be333201138b4f644dc545ecb (patch)
tree1f1c5b69133328267cfece9a41762713d78086da /compiler
parentaccf61dd42548bd5ec61d43f246b3eb499e980dd (diff)
parentaf39c0c57cb23678daaeb75ea729edd71aba14b8 (diff)
downloadrust-26f5936cf9f2d08be333201138b4f644dc545ecb.tar.gz
rust-26f5936cf9f2d08be333201138b4f644dc545ecb.zip
Rollup merge of #143719 - xizheyin:142812-1, r=jieyouxu
Emit warning when there is no space between `-o` and arg

Closes rust-lang/rust#142812

`getopt` doesn't seem to have an API to check this, so we have to check the args manually.

r? compiler
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs46
-rw-r--r--compiler/rustc_session/src/config.rs5
2 files changed, 51 insertions, 0 deletions
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 18490385455..f3ed6042105 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -1237,9 +1237,55 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<geto
         return None;
     }
 
+    warn_on_confusing_output_filename_flag(early_dcx, &matches, args);
+
     Some(matches)
 }
 
+/// Warn if `-o` is used without a space between the flag name and the value
+/// and the value is a high-value confusables,
+/// e.g. `-optimize` instead of `-o optimize`, see issue #142812.
+fn warn_on_confusing_output_filename_flag(
+    early_dcx: &EarlyDiagCtxt,
+    matches: &getopts::Matches,
+    args: &[String],
+) {
+    fn eq_ignore_separators(s1: &str, s2: &str) -> bool {
+        let s1 = s1.replace('-', "_");
+        let s2 = s2.replace('-', "_");
+        s1 == s2
+    }
+
+    if let Some(name) = matches.opt_str("o")
+        && let Some(suspect) = args.iter().find(|arg| arg.starts_with("-o") && *arg != "-o")
+    {
+        let filename = suspect.strip_prefix("-").unwrap_or(suspect);
+        let optgroups = config::rustc_optgroups();
+        let fake_args = ["optimize", "o0", "o1", "o2", "o3", "ofast", "og", "os", "oz"];
+
+        // Check if provided filename might be confusing in conjunction with `-o` flag,
+        // i.e. consider `-o{filename}` such as `-optimize` with `filename` being `ptimize`.
+        // There are high-value confusables, for example:
+        // - Long name of flags, e.g. `--out-dir` vs `-out-dir`
+        // - C compiler flag, e.g. `optimize`, `o0`, `o1`, `o2`, `o3`, `ofast`.
+        // - Codegen flags, e.g. `pt-level` of `-opt-level`.
+        if optgroups.iter().any(|option| eq_ignore_separators(option.long_name(), filename))
+            || config::CG_OPTIONS.iter().any(|option| eq_ignore_separators(option.name(), filename))
+            || fake_args.iter().any(|arg| eq_ignore_separators(arg, filename))
+        {
+            early_dcx.early_warn(
+                "option `-o` has no space between flag name and value, which can be confusing",
+            );
+            early_dcx.early_note(format!(
+                "output filename `-o {name}` is applied instead of a flag named `o{name}`"
+            ));
+            early_dcx.early_help(format!(
+                "insert a space between `-o` and `{name}` if this is intentional: `-o {name}`"
+            ));
+        }
+    }
+}
+
 fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
     let mut parser = unwrap_or_emit_fatal(match &sess.io.input {
         Input::File(file) => new_parser_from_file(&sess.psess, file, None),
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index a91e2140fd4..d6215e1de04 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1708,6 +1708,11 @@ impl RustcOptGroup {
             OptionKind::FlagMulti => options.optflagmulti(short_name, long_name, desc),
         };
     }
+
+    /// This is for diagnostics-only.
+    pub fn long_name(&self) -> &str {
+        self.long_name
+    }
 }
 
 pub fn make_opt(