Repository: clap-rs/clap
Pull request #3250 (2022-01-04 15:16:26)

fix(parser): Ignore Last when checking Hyphen Values 
This was found with #3249


Patch:
diff --git a/src/lib.rs b/src/lib.rs
index 02b4d682..fcc7b4fa 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,8 @@
 // HACK https://github.com/rust-lang/rust-clippy/issues/7290
 #![allow(clippy::single_component_path_imports)]
 #![allow(clippy::branches_sharing_code)]
+// Doesn't allow for debug statements, etc to be unique
+#![allow(clippy::if_same_then_else)]
 
 #[cfg(not(feature = "std"))]
 compile_error!("`std` feature is currently required to build `clap`");
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index d490f851..10e9b624 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -1181,17 +1181,27 @@ impl<'help, 'app> Parser<'help, 'app> {
         debug!("Parser::parse_short_arg: short_arg={:?}", short_arg);
         let arg = short_arg.to_str_lossy();
 
-        if (self.is_set(AS::AllowNegativeNumbers) && arg.parse::<f64>().is_ok())
-            || (self.is_set(AS::AllowHyphenValues)
-                && arg.chars().any(|c| !self.app.contains_short(c)))
-            || matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt)
+        #[allow(clippy::blocks_in_if_conditions)]
+        if self.is_set(AS::AllowNegativeNumbers) && arg.parse::<f64>().is_ok() {
+            debug!("Parser::parse_short_arg: negative number");
+            return ParseResult::MaybeHyphenValue;
+        } else if self.is_set(AS::AllowHyphenValues)
+            && arg.chars().any(|c| !self.app.contains_short(c))
+        {
+            debug!("Parser::parse_short_args: contains non-short flag");
+            return ParseResult::MaybeHyphenValue;
+        } else if matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt)
                 if self.app[opt].is_set(ArgSettings::AllowHyphenValues))
-            || self
-                .app
-                .args
-                .get(&pos_counter)
-                .map_or(false, |arg| arg.is_set(ArgSettings::AllowHyphenValues))
         {
+            debug!("Parser::parse_short_args: prior arg accepts hyphenated values",);
+            return ParseResult::MaybeHyphenValue;
+        } else if self.app.args.get(&pos_counter).map_or(false, |arg| {
+            arg.is_set(ArgSettings::AllowHyphenValues) && !arg.is_set(ArgSettings::Last)
+        }) {
+            debug!(
+                "Parser::parse_short_args: positional at {} allows hyphens",
+                pos_counter
+            );
             return ParseResult::MaybeHyphenValue;
         }
 


Test patch:
diff --git a/tests/builder/positionals.rs b/tests/builder/positionals.rs
index 6452aac2..e7f1a6d4 100644
--- a/tests/builder/positionals.rs
+++ b/tests/builder/positionals.rs
@@ -287,3 +287,24 @@ fn positional_arg_with_short() {
         .arg(Arg::new("arg").index(1).short('a'))
         .try_get_matches();
 }
+
+#[test]
+fn ignore_hyphen_values_on_last() {
+    let app = clap::App::new("foo")
+        .arg(
+            clap::Arg::new("cmd")
+                .multiple_values(true)
+                .last(true)
+                .allow_hyphen_values(true),
+        )
+        .arg(
+            clap::Arg::new("name")
+                .long("name")
+                .short('n')
+                .takes_value(true)
+                .required(false),
+        );
+
+    let matches = app.try_get_matches_from(["test", "-n", "foo"]).unwrap();
+    assert_eq!(matches.value_of("name"), Some("foo"));
+}