rust-clippy icon indicating copy to clipboard operation
rust-clippy copied to clipboard

Lint for `cfg` usage which would provide a misleading result in a build script.

Open thomcc opened this issue 3 years ago • 2 comments

What it does

Lints for uses of cfg!, #[cfg], #[cfg_attr] with keys which will have misleading[^1] values when used inside build scripts (e.g. build.rs files), such as windows, unix, and the ones that begin with target_*.

This is a restriction lint, as I'm assuming Clippy has no way of figuring out if we're in a build script on it's own (it seems completely impossible, as build scripts are just normal rust code).

The idea is that developers of build scripts that support cross compilation can add something like #![deny(clippy::misleading_cfg_in_build_script)].

[^1]: These will report the values for the host, not the target, e.g. cfg!(windows) is true when compiling on Windows, so will give the wrong answer if cross compiling. This is because build scripts run on the machine performing compilation, rather than on the target.

Lint Name

misleading_cfg_in_build_script

Category

restriction

Advantage

The recommended code produces the expected value even when cross-compiling.

Drawbacks

  • This is not a useful lint outside of build scripts. The advice it gives will be wrong in other environments.
  • Unlike with #[cfg], there may be no alternative for uses of #[cfg]/#[cfg_attr] (perhaps that code needs to not compile).
  • Even within a build scripts, it may be impossible or impractical to support cross compilation.
  • Even within build-scripts that support cross-compilation, you need to use cfg! to detect if you're cross-compiling.
  • The correct equivalent is significantly more complicated and much harder to write correctly.
  • It's an opt-in restriction lint, so the usefulness is inherently limited.

Example

The correct replacement is a bit tricky and has several cases, and IDK if Clippy should even suggest a replacement. Also, it's worth noting that none of these have any hope of working outside a build script.

Anyway, there are a few cases here:

For no-value cfgs, like cfg!(windows) or cfg!(unix)

if cfg!(windows) { ... }

Can be written as:

if std::env::var("CARGO_CFG_WINDOWS").is_ok() { ... }

For single-value cfgs, like cfg!(target_os = ...)

if cfg!(target_os = "macos") { ... }

Can be written as:

if std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default() == "macos" { ... }

For multi-value cfgs, like cfg!(target_feature = ...) or target_family

(This also works for single-value cfgs, but you can see why it's less desirable)

if cfg!(target_feature = "crt-static") { ... }

Can be written as:

if std::env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or_default().split(',').any(|f| f == "crt-static") { ... }

The complexity is caused by multi-value cfg being provided by cargo as comma-separated environment vars. This not a theoretical issue either (as crt-static is definitely useful for build scripts).

thomcc avatar Sep 02 '22 23:09 thomcc

I'm not sure if this is too niche, and will understand if it is. I've wanted it for a long time, though.

thomcc avatar Sep 03 '22 00:09 thomcc

I think this is still true(?): https://github.com/rust-lang/rust-clippy/issues/2802

smoelius avatar Sep 03 '22 01:09 smoelius

Related

  • #10224
  • #11649

rustc/cargo have an unstable option for checking cfgs generally but it treats all build units the same, including build.rs

  • https://github.com/rust-lang/rust/issues/82450
  • https://github.com/rust-lang/cargo/issues/10554

epage avatar Mar 04 '24 19:03 epage

Taking a look.

GuillaumeGomez avatar May 28 '24 09:05 GuillaumeGomez