From ad764b5dc4e244a0b180c3a5a298511d33b158be Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Sat, 12 Apr 2025 22:11:34 +0200 Subject: [PATCH] avoid using cfg with destination-crate feature in proc-macro derive Quoting cargo: using a cfg inside a derive macro will use the cfgs from the destination crate and not the ones from the defining crate In these two instances, the cfg was simply used to avoid emitting dead code if the "complete" feature is not desired. By passing the "complete" feature down to the derive crate and evaluating if there, we achieve the same goal, without having to deal with suprisingly-valued and unexpected cfgs downstream. Alternatively, we could just remove the feature-flag and make it part of the permanent API. --- Cargo.toml | 2 +- derive/Cargo.toml | 3 +++ derive/src/lib.rs | 48 ++++++++++++++++++++++++++++++++--------------- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e9c6517..5c9a356 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ lexopt = "0.3.0" [features] parse-is-complete = ["complete"] -complete = ["uutils-args-complete"] +complete = ["uutils-args-complete", "uutils-args-derive/complete"] [workspace] members = ["derive", "complete"] diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 0a2f39c..1e74932 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -12,3 +12,6 @@ proc-macro = true proc-macro2 = "1.0.81" quote = "1.0.36" syn = { version = "2.0.60", features = ["full"] } + +[features] +complete = [] diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 5f6d9d7..f26ff60 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -81,6 +81,17 @@ pub fn arguments(input: TokenStream) -> TokenStream { quote!(Ok(Some(::uutils_args::Argument::Positional(value)))) }; + let complete_fn = if cfg!(feature = "complete") { + quote!( + fn complete() -> ::uutils_args_complete::Command<'static> { + use ::uutils_args::Value; + #complete_command + } + ) + } else { + quote!() + }; + let expanded = quote!( impl #impl_generics Arguments for #name #ty_generics #where_clause { const EXIT_CODE: i32 = #exit_code; @@ -117,11 +128,10 @@ pub fn arguments(input: TokenStream) -> TokenStream { #version_string } - #[cfg(feature = "complete")] - fn complete() -> ::uutils_args_complete::Command<'static> { - use ::uutils_args::Value; - #complete_command - } + // "#[cfg(feature = "complete")] fn complete()" + // However, this would attempt to evaluate the feature "complete" in the context of the caller. + // Therefore, we must evaluate whether the feature is true or not while this macro is running. + #complete_fn } ); @@ -176,6 +186,22 @@ pub fn value(input: TokenStream) -> TokenStream { let keys_len = all_keys.len(); + let value_hint_fn = if cfg!(feature = "complete") { + quote!( + fn value_hint() -> ::uutils_args_complete::ValueHint { + let keys: [&str; #keys_len] = [#(#all_keys),*]; + ::uutils_args_complete::ValueHint::Strings( + keys + .into_iter() + .map(ToString::to_string) + .collect() + ) + } + ) + } else { + quote!() + }; + let expanded = quote!( impl #impl_generics Value for #name #ty_generics #where_clause { fn from_value(value: &::std::ffi::OsStr) -> ::uutils_args::ValueResult { @@ -212,16 +238,8 @@ pub fn value(input: TokenStream) -> TokenStream { }) } - #[cfg(feature = "complete")] - fn value_hint() -> ::uutils_args_complete::ValueHint { - let keys: [&str; #keys_len] = [#(#all_keys),*]; - ::uutils_args_complete::ValueHint::Strings( - keys - .into_iter() - .map(ToString::to_string) - .collect() - ) - } + // See impl Arguments::complete (fn value_hint()) + #value_hint_fn } );