Repository: clap-rs/clap
Pull request #3909 (2022-07-12 01:58:24)

fix: loosen lifetime constraint on mut_subcommand
follow-up to PR #3882, as discussed in that PR's comments `mut_subcommand` does not resolve aliases and, if an alias name is passed for `subcmd_id`, would trigger invalid behavior as per #3888.

thus to avoid this, one might resolve the alias name to the actual name before calling `mut_subcommand`, but this is currently almost impossible to do due to `mut_subcommand` signature's with the `'help` lifetime. specifically, looking at the test code included in this PR and without the lifetime changes, we get the following error:

```
error[E0597]: `true_name` does not live long enough
   --> tests/builder/tests.rs:459:32
    |
459 |     cmd = cmd.mut_subcommand(&*true_name, |subcmd| subcmd.about("modified about"));
    |                                ^^^^^^^^^ borrowed value does not live long enough
460 |     assert_eq!(cmd.find_subcommand("baz").unwrap().get_about().unwrap(), "modified about");
461 | }
    | -
    | |
    | `true_name` dropped here while still borrowed
    | borrow might be used here, when `cmd` is dropped and runs the destructor for type `App<'_>`
    |
    = note: values in a scope are dropped in the opposite order they are defined

For more information about this error, try `rustc --explain E0597`.
error: could not compile `clap` due to previous error
```

but loosening the lifetime constraint from `'help` to `'a` resolves this. 

note, directly using `cmd.find_subcommand("baz").unwrap().get_name()` is invalid because of:
```
error[E0597]: `cmd` does not live long enough
   --> tests/builder/tests.rs:457:21
    |
457 |     let true_name = cmd.find_subcommand("baz").unwrap().get_name();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
465 | }
    | -
    | |
    | `cmd` dropped here while still borrowed
    | borrow might be used here, when `cmd` is dropped and runs the destructor for type `App<'_>`

error[E0506]: cannot assign to `cmd` because it is borrowed
   --> tests/builder/tests.rs:460:5
    |
457 |     let true_name = cmd.find_subcommand("baz").unwrap().get_name();
    |                     -------------------------- borrow of `cmd` occurs here
...
460 |     cmd = cmd.mut_subcommand(true_name, |subcmd| subcmd.about("modified about"));
    |     ^^^
    |     |
    |     assignment to borrowed `cmd` occurs here
    |     borrow later used here

error[E0505]: cannot move out of `cmd` because it is borrowed
   --> tests/builder/tests.rs:460:11
    |
457 |     let true_name = cmd.find_subcommand("baz").unwrap().get_name();
    |                     -------------------------- borrow of `cmd` occurs here
...
460 |     cmd = cmd.mut_subcommand(true_name, |subcmd| subcmd.about("modified about"));
    |           ^^^                --------- borrow later used here
    |           |
    |           move out of `cmd` occurs here

Some errors have detailed explanations: E0505, E0506, E0597.
For more information about an error, try `rustc --explain E0505`.
error: could not compile `clap` due to 3 previous errors
```

I'd also like to do this for `mut_arg`, but that's a more involved change so will put that in a separate PR. :) 





Patch:
diff --git a/src/builder/command.rs b/src/builder/command.rs
index d644cb76..9ef4947c 100644
--- a/src/builder/command.rs
+++ b/src/builder/command.rs
@@ -298,10 +298,10 @@ impl<'help> App<'help> {
     /// assert!(res.is_ok());
     /// ```
     #[must_use]
-    pub fn mut_subcommand<T, F>(mut self, subcmd_id: T, f: F) -> Self
+    pub fn mut_subcommand<'a, T, F>(mut self, subcmd_id: T, f: F) -> Self
     where
         F: FnOnce(App<'help>) -> App<'help>,
-        T: Into<&'help str>,
+        T: Into<&'a str>,
     {
         let subcmd_id: &str = subcmd_id.into();
         let id = Id::from(subcmd_id);


Test patch:
diff --git a/tests/builder/tests.rs b/tests/builder/tests.rs
index 196884e0..8a1544c1 100644
--- a/tests/builder/tests.rs
+++ b/tests/builder/tests.rs
@@ -445,6 +445,25 @@ fn mut_subcommand_all() {
     );
 }
 
+#[test]
+fn mut_subcommand_with_alias_resolve() {
+    let mut cmd =
+        Command::new("foo").subcommand(Command::new("bar").alias("baz").about("test subcmd"));
+    assert_eq!(
+        cmd.find_subcommand("baz").unwrap().get_about().unwrap(),
+        "test subcmd"
+    );
+
+    let true_name = cmd.find_subcommand("baz").unwrap().get_name().to_string();
+    assert_eq!(true_name, "bar");
+
+    cmd = cmd.mut_subcommand(&*true_name, |subcmd| subcmd.about("modified about"));
+    assert_eq!(
+        cmd.find_subcommand("baz").unwrap().get_about().unwrap(),
+        "modified about"
+    );
+}
+
 #[test]
 fn issue_3669_command_build_recurses() {
     let mut cmd = Command::new("ctest").subcommand(