crate A depends on L, using feature F, but not enabling the feature in Cargo.toml
crate B depends on A and L, enabling feature F in it's Cargo.toml
crate A on itself does not build succesfully
crate B builds just fine
If I understand correctly, this is a side-effect of feature unification, but I highly doubt this is intended.
It's worse for workspaces. A and B can be independent crates sharing a workspace. A can have feature F enabled for crate D. B may depend on D without enabling F, but if the workspace is built as a whole, B can still use F. If B is compiled separately from a clean build with cargo build --package B, compilation fails.
Features are a bit of weak point of Cargo/Rust. You're totally right that it's very easy to accidentally depend on a feature without enabling it explicitly. Also agreed that it's even worse in workspaces.
Some crates also break the expectation of features being additive making it pretty easy to get into situations where two parts of your dep graph end up enabling incompatible features of a crate.
I would much prefer if there was a better way to detect when a feature is accidentally depended upon or when features conflict with each other. For a long time, conditional compilation was applied too early in the Rust build and then the information was lost, but recent updates track a bit more info about features and conditional compilation to improve diagnostics. I hope that it continues in this direction with better tracking of features.
Another fun one is if you have a macro that generates different code depending on feature flags set. And if that generated code assumes that the same feature flags are set during source compilation. This means that a build dependency enabling a feature flag for the crate with the macro can break compilation because thar feature flag is not necessarily set during source compilation.
If we had a way to allow the generated code to know what features are enabled in the library you are generating code against, that'd be great (similarly, we need a $crate for proc macros).
I handled this in one crate by shimming almost everything through a macro_rules! defined in the runtime crate. It works, even if it isn't the nicest to set up if you want the proc macro to directly process any more than a single feature flag.
Please (you can shim it for functionlike macros but attributes have no choice but to assume and/or cheat)
It somehow works, unless someone enables the feature behind my back through another path in the dependency tree. Then I'd get error in my crate that the do_abc is not implemented, or that Abc is not matched.
Suggestion
What i'd suggest would be a way to write #[cfg(feature="<crate>/<feature>")] like so:
This would also help for #14415 above as proc-macros could generate code containing #[cfg(feature="main_crate/some_feature")]
To make it work, all the enabled features of a crate would have to be stored in the crate metadata. (as well as the possible features for check-cfg)
This would have the drawback to make the cfg(feature=...) a bit special compared to other cfg in rustc, but i think this would be worth it.