Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,9 @@ feature list is not an exhaustive audit.
arity with distinct `value_names`); optional-value shapes that need
per-occurrence presence semantics and arbitrary `value_parser` callbacks
produce targeted migration diagnostics instead of a generic unknown-option error.
The generated build also takes a zero-cost shared reference to every field, so a
parse-only compatibility flag does not trip an adopter's `deny(dead_code)` merely
because application code intentionally accepts and ignores it.
- [x] **Command-with-arguments completion hints.** `ExecutablePath`,
`CommandName`, `CommandString`, and `CommandWithArguments` lower to
shell-native completion types. A forwarded argv vector offers commands for
Expand Down
15 changes: 12 additions & 3 deletions derive/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,19 @@ fn built_value(cli: &Cli, sub_build: &TokenStream, fields: &[TokenStream]) -> To
debug_assert!(cli.fields.is_empty());
quote!(Self)
} else {
let field_reads = cli.fields.iter().map(|field| &field.ident);
quote! {
Self {
#sub_build
#(#fields),*
{
let __usage_built = Self {
#sub_build
#(#fields),*
};
// A parsed compatibility flag may intentionally be accepted and ignored.
// Reading every field here keeps `#[deny(dead_code)]` from blaming the
// adopter for a field the derive itself owns. These shared references
// generate no runtime work after optimization.
#(let _ = &__usage_built.#field_reads;)*
__usage_built
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions usage-rs/tests/facade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,15 @@ struct ClapSpellings {
path: Option<String>,
}

#[deny(dead_code)]
#[derive(Cli)]
#[command(bin = "parse-only-field")]
struct ParseOnlyField {
/// Accepted for compatibility even though the application intentionally ignores it.
#[arg(long)]
compatibility: bool,
}

#[derive(Cli)]
#[command(bin = "clap-override-id")]
struct ClapOverrideId {
Expand Down Expand Up @@ -1121,6 +1130,12 @@ fn clap_field_ids_and_aliases_need_no_rewrite() {
);
}

#[test]
fn parse_only_fields_do_not_trigger_dead_code() {
ParseOnlyField::parse_from(&[OsStr::new("--compatibility")])
.expect("the compatibility flag should still parse");
}

#[test]
fn clap_value_arity_stays_on_each_flag_occurrence() {
let parsed = FixedArity::parse_from(&[
Expand Down
Loading