Summary
In 0.14.2 the Patch derive emits #[cfg(feature = "nesting")] / #[cfg(not(feature = "nesting"))] attributes into the generated code. Those cfgs are then evaluated against the consuming crate's feature set rather than struct-patch's, so any downstream crate that doesn't happen to define a feature called nesting trips rustc's unexpected_cfgs lint. Under -D warnings (or RUSTFLAGS="-D warnings" in CI) this is a hard build failure.
This is a regression in 0.14.2; 0.14.1 is fine.
Reproduction
Any crate that derives Patch and builds with -D warnings:
# Cargo.toml
[dependencies]
struct-patch = "0.14.2"
use struct_patch::Patch;
#[derive(Clone, Debug, PartialEq, Patch)]
pub struct Config {
pub host: String,
pub port: u16,
}
$ cargo clippy --all-targets -- -D warnings
error: unexpected `cfg` condition value: `nesting`
--> src/lib.rs:3:36
|
3 | #[derive(Clone, Debug, PartialEq, Patch)]
| ^^^^^
|
= note: expected values for `feature` are: (the consuming crate's own features)
= note: using a cfg inside a derive macro will use the cfgs from the destination crate
and not the ones from the defining crate
= help: try referring to `Patch` crate for guidance on how handle this unexpected cfg
= note: `-D unexpected-cfgs` implied by `-D warnings`
= note: this error originates in the derive macro `Patch`
Enabling features = ["nesting"] on the struct-patch dependency does not help, since the cfg is resolved in the downstream crate, which still has no such feature.
Cause
Introduced by #184 (add log on nesting feature, merged 2026-09-12, released as 0.14.2).
The attributes were placed inside the quote! { .. } block instead of outside it, so they end up in the macro output rather than gating the code generation. In derive/src/patch.rs, inside let patch_impl = quote! { (opens at line 830):
derive/src/patch.rs:887 — #[cfg(not(feature = "nesting"))] on fn apply_with_log<F: FnMut(&str)>
derive/src/patch.rs:941 — #[cfg(feature = "nesting")] on fn apply_with_log<F: FnMut(&[&str], &str)>
The same pattern appears inside let box_impl = quote! { (opens at line 1138):
derive/src/patch.rs:1147 and derive/src/patch.rs:1156
derive/src/filler.rs picked up equivalents in the same PR (e.g. line 323 inside its quote! block).
For contrast, the neighbouring uses in the same file are correct — e.g. patch.rs:771/:782 and :808/:827 sit outside quote! and gate a TokenStream variable, which is the intended shape.
Effect on generated code
Because feature = "nesting" is essentially never set in a consumer crate, the #[cfg(feature = "nesting")] arm is always dropped and the non-nesting arm always selected — so users who enable struct-patch/nesting silently get the non-nesting apply_with_log signature, in addition to the lint error everyone else gets.
Suggested fix
Move the gating out of quote!, following the existing pattern at patch.rs:771-827: build the two apply_with_log implementations into a TokenStream selected by a proc-macro-level #[cfg], and interpolate a single #apply_with_log_impl into quote!.
Related
#73 is the same class of bug for the merge feature.
Summary
In 0.14.2 the
Patchderive emits#[cfg(feature = "nesting")]/#[cfg(not(feature = "nesting"))]attributes into the generated code. Thosecfgs are then evaluated against the consuming crate's feature set rather thanstruct-patch's, so any downstream crate that doesn't happen to define a feature callednestingtrips rustc'sunexpected_cfgslint. Under-D warnings(orRUSTFLAGS="-D warnings"in CI) this is a hard build failure.This is a regression in 0.14.2; 0.14.1 is fine.
Reproduction
Any crate that derives
Patchand builds with-D warnings:Enabling
features = ["nesting"]on thestruct-patchdependency does not help, since thecfgis resolved in the downstream crate, which still has no such feature.Cause
Introduced by #184 (
add log on nesting feature, merged 2026-09-12, released as 0.14.2).The attributes were placed inside the
quote! { .. }block instead of outside it, so they end up in the macro output rather than gating the code generation. Inderive/src/patch.rs, insidelet patch_impl = quote! {(opens at line 830):derive/src/patch.rs:887—#[cfg(not(feature = "nesting"))]onfn apply_with_log<F: FnMut(&str)>derive/src/patch.rs:941—#[cfg(feature = "nesting")]onfn apply_with_log<F: FnMut(&[&str], &str)>The same pattern appears inside
let box_impl = quote! {(opens at line 1138):derive/src/patch.rs:1147andderive/src/patch.rs:1156derive/src/filler.rspicked up equivalents in the same PR (e.g. line 323 inside itsquote!block).For contrast, the neighbouring uses in the same file are correct — e.g.
patch.rs:771/:782and:808/:827sit outsidequote!and gate aTokenStreamvariable, which is the intended shape.Effect on generated code
Because
feature = "nesting"is essentially never set in a consumer crate, the#[cfg(feature = "nesting")]arm is always dropped and the non-nesting arm always selected — so users who enablestruct-patch/nestingsilently get the non-nestingapply_with_logsignature, in addition to the lint error everyone else gets.Suggested fix
Move the gating out of
quote!, following the existing pattern atpatch.rs:771-827: build the twoapply_with_logimplementations into aTokenStreamselected by a proc-macro-level#[cfg], and interpolate a single#apply_with_log_implintoquote!.Related
#73 is the same class of bug for the
mergefeature.