Repository: clap-rs/clap
Pull request #3505 (2022-02-23 15:23:59)

fix(derive): Allow other attributes with subcommand that has subcommands
This was overlooked when we added support for `#[clap(subcommand)]` to
variants.

Fixes https://github.com/clap-rs/clap/issues/3504


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0fe3f3f4..a224a3c4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -125,7 +125,7 @@ jobs:
         override: true
     - uses: Swatinem/rust-cache@v1
     - name: UI Tests
-      run: cargo test --test derive_ui --features derive
+      run: make test-ui
   docs:
     name: Docs
     runs-on: ubuntu-latest
diff --git a/Makefile b/Makefile
index e001b23f..35f188b3 100644
--- a/Makefile
+++ b/Makefile
@@ -10,6 +10,8 @@ ifneq (${TOOLCHAIN_TARGET},)
   ARGS+=--target ${TOOLCHAIN_TARGET}
 endif
 
+MSRV?=1.54.0
+
 _FEATURES = minimal default wasm full debug release
 _FEATURES_minimal = --no-default-features --features "std"
 _FEATURES_default =
@@ -29,3 +31,6 @@ test-%:
 
 clippy-%:
 	cargo clippy ${_FEATURES_${@:clippy-%=%}} ${ARGS} --all-targets -- -D warnings -A deprecated
+
+test-ui:
+	cargo +${MSRV} test --test derive_ui --features derive
diff --git a/clap_derive/src/attrs.rs b/clap_derive/src/attrs.rs
index b51eeaad..9a7bb7e7 100644
--- a/clap_derive/src/attrs.rs
+++ b/clap_derive/src/attrs.rs
@@ -127,15 +127,9 @@ impl Attrs {
                         "parse attribute is not allowed for subcommand"
                     );
                 }
-                if res.has_explicit_methods() {
-                    abort!(
-                        res.kind.span(),
-                        "methods in attributes are not allowed for subcommand"
-                    );
-                }
+
                 use syn::Fields::*;
                 use syn::FieldsUnnamed;
-
                 let field_ty = match variant.fields {
                     Named(_) => {
                         abort!(variant.span(), "structs are not allowed for subcommand");
diff --git a/examples/README.md b/examples/README.md
index 783948e3..690789d7 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -32,4 +32,4 @@ New examples:
 - Testing: Ensure there is a markdown file with [trycmd](https://docs.rs/trycmd) syntax
 - Link the `.md` file from here
 
-See also the general [CONTRIBUTING](../CONTRIBUTING.md).
+See also the general [CONTRIBUTING](../../CONTRIBUTING.md).


Test patch:
diff --git a/tests/derive/subcommands.rs b/tests/derive/subcommands.rs
index 3d122c85..0986df57 100644
--- a/tests/derive/subcommands.rs
+++ b/tests/derive/subcommands.rs
@@ -296,7 +296,9 @@ fn external_subcommand_optional() {
 fn enum_in_enum_subsubcommand() {
     #[derive(Parser, Debug, PartialEq)]
     pub enum Opt {
-        #[clap(subcommand)]
+        #[clap(alias = "l")]
+        List,
+        #[clap(subcommand, alias = "d")]
         Daemon(DaemonCommand),
     }
 
@@ -309,11 +311,20 @@ fn enum_in_enum_subsubcommand() {
     let result = Opt::try_parse_from(&["test"]);
     assert!(result.is_err());
 
+    let result = Opt::try_parse_from(&["test", "list"]).unwrap();
+    assert_eq!(Opt::List, result);
+
+    let result = Opt::try_parse_from(&["test", "l"]).unwrap();
+    assert_eq!(Opt::List, result);
+
     let result = Opt::try_parse_from(&["test", "daemon"]);
     assert!(result.is_err());
 
     let result = Opt::try_parse_from(&["test", "daemon", "start"]).unwrap();
     assert_eq!(Opt::Daemon(DaemonCommand::Start), result);
+
+    let result = Opt::try_parse_from(&["test", "d", "start"]).unwrap();
+    assert_eq!(Opt::Daemon(DaemonCommand::Start), result);
 }
 
 #[test]