Repository: clap-rs/clap (rust)

Issue:
`arg` macro fails to accept quoted strings
### Please complete the following tasks

- [x] I have searched the [discussions](https://github.com/clap-rs/clap/discussions)
- [x] I have searched the [open](https://github.com/clap-rs/clap/issues) and [rejected](https://github.com/clap-rs/clap/issues?q=is%3Aissue+label%3AS-wont-fix+is%3Aclosed) issues

### Rust Version

rustc 1.84.0 (9fc6b4312 2025-01-07)

### Clap Version

clap = { version = "4.5.39", features = ["cargo"] }

### Minimal reproducible code

```rust
use clap::{arg};

fn main() {
//let a = arg!(config-file: --config <FILE>);
//             ^^^^^^ no rules expected this token in macro call
//let a = arg!("config-file": --config <FILE>);
//                          ^ no rules expected this token in macro call
//let a = arg!("configfile": --config <FILE>);
//                         ^ no rules expected this token in macro call
let a = arg!(configfile: --config <FILE>); // Works
println!("{a:#?}")
}
```


### Steps to reproduce the bug with the above code

`cargo run`

### Actual Behaviour

```rust
//let a = arg!(config-file: --config <FILE>);
//             ^^^^^^ no rules expected this token in macro call
//let a = arg!("config-file": --config <FILE>);
//                          ^ no rules expected this token in macro call
//let a = arg!("configfile": --config <FILE>);
//                         ^ no rules expected this token in macro call
```


### Expected Behaviour

No compilation errors.

### Additional Context

The [docs](https://docs.rs/clap/latest/clap/macro.arg.html) say:
>The name may be either a bare-word or a string, followed by a :, like name: or "name":.

### Debug Output

_No response_

Pull request description:
feat(builder): Allow quoted id's for arg macro
Fixes #6024

<!--
Thanks for helping out!

Please link the appropriate issue from your PR.

If you don't have an issue, we'd recommend starting with one first so the PR can focus on the
implementation (unless its an obvious bug or documentation fix that will have
little conversation).
-->


Patch:
diff --git a/clap_builder/src/macros.rs b/clap_builder/src/macros.rs
index c03d3237..a15b6f36 100644
--- a/clap_builder/src/macros.rs
+++ b/clap_builder/src/macros.rs
@@ -530,6 +530,14 @@ macro_rules! arg_impl {
 /// [`Arg`]: crate::Arg
 #[macro_export]
 macro_rules! arg {
+    ( -$($tail:tt)+ ) => {{
+        let arg = $crate::Arg::default();
+        let arg = $crate::arg_impl! {
+            @arg (arg) -$($tail)+
+        };
+        debug_assert_ne!(arg.get_id(), "", "Without a value or long flag, the `name:` prefix is required");
+        arg
+    }};
     ( $name:ident: $($tail:tt)+ ) => {{
         let arg = $crate::Arg::new($crate::arg_impl! { @string $name });
         let arg = $crate::arg_impl! {
@@ -537,6 +545,13 @@ macro_rules! arg {
         };
         arg
     }};
+    ( $name:literal: $($tail:tt)+ ) => {{
+        let arg = $crate::Arg::new($crate::arg_impl! { @string $name });
+        let arg = $crate::arg_impl! {
+            @arg (arg) $($tail)+
+        };
+        arg
+    }};
     ( $($tail:tt)+ ) => {{
         let arg = $crate::Arg::default();
         let arg = $crate::arg_impl! {
diff --git a/src/_faq.rs b/src/_faq.rs
index 02657848..7ca5551a 100644
--- a/src/_faq.rs
+++ b/src/_faq.rs
@@ -69,7 +69,7 @@
 //!
 //! There are also experiments with other APIs:
 //! - [fncmd](https://github.com/yuhr/fncmd): function attribute
-//! - [clap-serde](https://github.com/aobatact/clap-serde): create a `Command` from a deserializer
+//! - [clap-serde](https://github.com/aobatact/clap-serde): create an `Command` from a deserializer
 //!
 //! ### When should I use the builder vs derive APIs?
 //!
@@ -80,7 +80,7 @@
 //!
 //! The [Builder API][crate::_tutorial] is a lower-level API that someone might want to use for
 //! - Faster compile times if you aren't already using other procedural macros
-//! - More flexibility, e.g. you can look up the [argument's values][crate::ArgMatches::get_many],
+//! - More flexibility, e.g. you can look up an [arguments values][crate::ArgMatches::get_many],
 //!   their [ordering with other arguments][crate::ArgMatches::indices_of], and [what set
 //!   them][crate::ArgMatches::value_source].  The Derive API can only report values and not
 //!   indices of or other data.


Test patch:
diff --git a/tests/builder/macros.rs b/tests/builder/macros.rs
new file mode 100644
index 00000000..0c894e18
--- /dev/null
+++ b/tests/builder/macros.rs
@@ -0,0 +1,359 @@
+use clap::arg;
+use snapbox::assert_data_eq;
+use snapbox::prelude::*;
+use snapbox::str;
+
+#[test]
+fn arg_long() {
+    let arg = arg!(--long);
+    assert_data_eq!(arg.get_id().to_debug(), str![[r#"
+"long"
+
+"#]]);
+    assert_data_eq!(arg.get_short().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_long().to_debug(), str![[r#"
+Some(
+    "long",
+)
+
+"#]]);
+    assert_data_eq!(arg.get_action().to_debug(), str![[r#"
+SetTrue
+
+"#]]);
+    assert_data_eq!(arg.is_required_set().to_debug(), str![[r#"
+false
+
+"#]]);
+    assert_data_eq!(arg.get_value_names().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_help().to_debug(), str![[r#"
+None
+
+"#]]);
+}
+
+#[test]
+#[should_panic = "Without a value or long flag, the `name:` prefix is required"]
+fn arg_short() {
+    arg!(-s);
+}
+
+#[test]
+fn arg_long_dashed() {
+    let arg = arg!(--"long-flag");
+    assert_data_eq!(arg.get_id().to_debug(), str![[r#"
+"long-flag"
+
+"#]]);
+    assert_data_eq!(arg.get_short().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_long().to_debug(), str![[r#"
+Some(
+    "long-flag",
+)
+
+"#]]);
+    assert_data_eq!(arg.get_action().to_debug(), str![[r#"
+SetTrue
+
+"#]]);
+    assert_data_eq!(arg.is_required_set().to_debug(), str![[r#"
+false
+
+"#]]);
+    assert_data_eq!(arg.get_value_names().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_help().to_debug(), str![[r#"
+None
+
+"#]]);
+}
+
+#[test]
+fn arg_long_optional_value() {
+    let arg = arg!(--long[VALUE]);
+    assert_data_eq!(arg.get_id().to_debug(), str![[r#"
+"long"
+
+"#]]);
+    assert_data_eq!(arg.get_short().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_long().to_debug(), str![[r#"
+Some(
+    "long",
+)
+
+"#]]);
+    assert_data_eq!(arg.get_action().to_debug(), str![[r#"
+Set
+
+"#]]);
+    assert_data_eq!(arg.is_required_set().to_debug(), str![[r#"
+false
+
+"#]]);
+    assert_data_eq!(arg.get_value_names().to_debug(), str![[r#"
+Some(
+    [
+        "VALUE",
+    ],
+)
+
+"#]]);
+    assert_data_eq!(arg.get_help().to_debug(), str![[r#"
+None
+
+"#]]);
+}
+
+#[test]
+fn arg_long_required_value() {
+    let arg = arg!(--long <VALUE>);
+    assert_data_eq!(arg.get_id().to_debug(), str![[r#"
+"long"
+
+"#]]);
+    assert_data_eq!(arg.get_short().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_long().to_debug(), str![[r#"
+Some(
+    "long",
+)
+
+"#]]);
+    assert_data_eq!(arg.get_action().to_debug(), str![[r#"
+Set
+
+"#]]);
+    assert_data_eq!(arg.is_required_set().to_debug(), str![[r#"
+false
+
+"#]]);
+    assert_data_eq!(arg.get_value_names().to_debug(), str![[r#"
+Some(
+    [
+        "VALUE",
+    ],
+)
+
+"#]]);
+    assert_data_eq!(arg.get_help().to_debug(), str![[r#"
+None
+
+"#]]);
+}
+
+#[test]
+#[should_panic = "Multiple values not yet supported"]
+fn arg_long_multiple_values() {
+    arg!(--long <VALUE1> <VALUE2> <VALUE3> [VALUE4] [VALUE5]);
+}
+
+#[test]
+fn arg_optional_value() {
+    let arg = arg!([VALUE]);
+    assert_data_eq!(arg.get_id().to_debug(), str![[r#"
+"VALUE"
+
+"#]]);
+    assert_data_eq!(arg.get_short().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_long().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_action().to_debug(), str![[r#"
+Set
+
+"#]]);
+    assert_data_eq!(arg.is_required_set().to_debug(), str![[r#"
+false
+
+"#]]);
+    assert_data_eq!(arg.get_value_names().to_debug(), str![[r#"
+Some(
+    [
+        "VALUE",
+    ],
+)
+
+"#]]);
+    assert_data_eq!(arg.get_help().to_debug(), str![[r#"
+None
+
+"#]]);
+}
+
+#[test]
+fn arg_required_value() {
+    let arg = arg!(<VALUE>);
+    assert_data_eq!(arg.get_id().to_debug(), str![[r#"
+"VALUE"
+
+"#]]);
+    assert_data_eq!(arg.get_short().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_long().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_action().to_debug(), str![[r#"
+Set
+
+"#]]);
+    assert_data_eq!(arg.is_required_set().to_debug(), str![[r#"
+true
+
+"#]]);
+    assert_data_eq!(arg.get_value_names().to_debug(), str![[r#"
+Some(
+    [
+        "VALUE",
+    ],
+)
+
+"#]]);
+    assert_data_eq!(arg.get_help().to_debug(), str![[r#"
+None
+
+"#]]);
+}
+
+#[test]
+#[should_panic = "Multiple values not yet supported"]
+fn arg_multiple_values() {
+    arg!(<VALUE1> <VALUE2> <VALUE3> [VALUE4] [VALUE5]);
+}
+
+#[test]
+fn arg_named_positional() {
+    let arg = arg!(name: <VALUE>);
+    assert_data_eq!(arg.get_id().to_debug(), str![[r#"
+"name"
+
+"#]]);
+    assert_data_eq!(arg.get_short().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_long().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_action().to_debug(), str![[r#"
+Set
+
+"#]]);
+    assert_data_eq!(arg.is_required_set().to_debug(), str![[r#"
+true
+
+"#]]);
+    assert_data_eq!(arg.get_value_names().to_debug(), str![[r#"
+Some(
+    [
+        "VALUE",
+    ],
+)
+
+"#]]);
+    assert_data_eq!(arg.get_help().to_debug(), str![[r#"
+None
+
+"#]]);
+}
+
+#[test]
+fn arg_named_long() {
+    let arg = arg!(name: --long <VALUE>);
+    assert_data_eq!(arg.get_id().to_debug(), str![[r#"
+"name"
+
+"#]]);
+    assert_data_eq!(arg.get_short().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_long().to_debug(), str![[r#"
+Some(
+    "long",
+)
+
+"#]]);
+    assert_data_eq!(arg.get_action().to_debug(), str![[r#"
+Set
+
+"#]]);
+    assert_data_eq!(arg.is_required_set().to_debug(), str![[r#"
+false
+
+"#]]);
+    assert_data_eq!(arg.get_value_names().to_debug(), str![[r#"
+Some(
+    [
+        "VALUE",
+    ],
+)
+
+"#]]);
+    assert_data_eq!(arg.get_help().to_debug(), str![[r#"
+None
+
+"#]]);
+}
+
+#[test]
+fn arg_named_dashed() {
+    let arg = arg!("named-arg": --long <VALUE>);
+    assert_data_eq!(arg.get_id().to_debug(), str![[r#"
+"named-arg"
+
+"#]]);
+    assert_data_eq!(arg.get_short().to_debug(), str![[r#"
+None
+
+"#]]);
+    assert_data_eq!(arg.get_long().to_debug(), str![[r#"
+Some(
+    "long",
+)
+
+"#]]);
+    assert_data_eq!(arg.get_action().to_debug(), str![[r#"
+Set
+
+"#]]);
+    assert_data_eq!(arg.is_required_set().to_debug(), str![[r#"
+false
+
+"#]]);
+    assert_data_eq!(arg.get_value_names().to_debug(), str![[r#"
+Some(
+    [
+        "VALUE",
+    ],
+)
+
+"#]]);
+    assert_data_eq!(arg.get_help().to_debug(), str![[r#"
+None
+
+"#]]);
+}