Repository: clap-rs/clap (language: rust)

Issue:
Does MSRV apply to clap_complete's unstable-dynamic feature?
### 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

1.74.0

### Clap Version

master

### Minimal reproducible code

N/A

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

in the clap repo:
```sh
RUSTUP_TOOLCHAIN=1.74.0 cargo check --workspace --all-features
```

### Actual Behaviour

```
error[E0599]: no method named `split_at_checked` found for reference `&str` in the current scope
   --> clap_complete/src/engine/complete.rs:395:10
    |
394 |       let (prefix, value) = value
    |  ___________________________-
395 | |         .split_at_checked(pos + delimiter.len_utf8())
    | |         -^^^^^^^^^^^^^^^^ help: there is a method with a similar name: `split_at`
    | |_________|
    |
```

### Expected Behaviour

Should clap_complete compile with the MSRV and `unstable-dynamic` turned on? Or is the unstable feature exempt from the MSRV policy?

### Additional Context

The MSRV policy is 2 minor versions, behind, so 1.80 (which is required for `split_at_checked`) is within that policy. So it would be possible to bump the MSRV to fix it, if desired.

If `unstable-dynamic` should be covered by the MSRV policy, I think this could be checked in CI by extending this line in the Makefile:
```
_FEATURES_full = --features "deprecated derive cargo env unicode string wrap_help unstable-ext unstable-dynamic"
```

MSRV violations are also caught by clippy since 1.78:

```sh
cargo clippy --workspace --all-features
```

```
warning: current MSRV (Minimum Supported Rust Version) is `1.74.0` but this item is stable since `1.80.0`
   --> clap_complete/src/engine/complete.rs:395:10
    |
395 |         .split_at_checked(pos + delimiter.len_utf8())
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv
    = note: `#[warn(clippy::incompatible_msrv)]` on by default
```

(plus two unrelated clippy warnings in the `unstable-dynamic` code)

Exentding the Makefile as suggested would also surface these three clippy warnings as errors.

### Debug Output

_No response_


Discussion / hints:

Fix patch:
diff --git a/clap_complete/src/engine/complete.rs b/clap_complete/src/engine/complete.rs
index c5ff943dbc5..9907749970d 100644
--- a/clap_complete/src/engine/complete.rs
+++ b/clap_complete/src/engine/complete.rs
@@ -391,9 +391,7 @@ fn rsplit_delimiter<'s, 'o>(
     let delimiter = delimiter?;
     let value = value.ok()?;
     let pos = value.rfind(delimiter)?;
-    let (prefix, value) = value
-        .split_at_checked(pos + delimiter.len_utf8())
-        .expect("since delimiter was found, it is within bounds");
+    let (prefix, value) = value.split_at(pos + delimiter.len_utf8());
     Some((Some(prefix), Ok(value)))
 }
 
diff --git a/clap_complete/src/engine/custom.rs b/clap_complete/src/engine/custom.rs
index db0eb2a17c9..a81f0b02848 100644
--- a/clap_complete/src/engine/custom.rs
+++ b/clap_complete/src/engine/custom.rs
@@ -213,6 +213,7 @@ where
 /// ```
 pub struct PathCompleter {
     current_dir: Option<std::path::PathBuf>,
+    #[allow(clippy::type_complexity)]
     filter: Option<Box<dyn Fn(&std::path::Path) -> bool + Send + Sync>>,
     stdio: bool,
 }
diff --git a/clap_complete/src/env/mod.rs b/clap_complete/src/env/mod.rs
index ea33d8d68a3..2c5049bdb87 100644
--- a/clap_complete/src/env/mod.rs
+++ b/clap_complete/src/env/mod.rs
@@ -250,15 +250,16 @@ impl<'s, F: Fn() -> clap::Command> CompleteEnv<'s, F> {
         let name = name.to_string_lossy();
 
         let shell = self.shells.completer(&name).ok_or_else(|| {
-            let shells = self
-                .shells
-                .names()
-                .enumerate()
-                .map(|(i, name)| {
-                    let prefix = if i == 0 { "" } else { ", " };
-                    format!("{prefix}`{name}`")
-                })
-                .collect::<String>();
+            let shells =
+                self.shells
+                    .names()
+                    .enumerate()
+                    .fold(String::new(), |mut seed, (i, name)| {
+                        use std::fmt::Write as _;
+                        let prefix = if i == 0 { "" } else { ", " };
+                        let _ = write!(&mut seed, "{prefix}`{name}`");
+                        seed
+                    });
             std::io::Error::new(
                 std::io::ErrorKind::Other,
                 format!("unknown shell `{name}`, expected one of {shells}"),


Test patch: