check-cfg

The tracking issue for this feature is: #82450.


This feature enables checking of conditional configuration.

rustc accepts the --check-cfg option, which specifies whether to check conditions and how to check them. The --check-cfg option takes a value, called the check cfg specification. This specification has one form:

  1. --check-cfg cfg(...) mark a configuration and it's expected values as expected.

No implicit expectation is added when using --cfg. Users are expected to pass all expected names and values using the check cfg specification.

The cfg(...) form

The cfg(...) form enables checking the values within list-valued conditions. It has this basic form:

rustc --check-cfg 'cfg(name, values("value1", "value2", ... "valueN"))'

where name is a bare identifier (has no quotes) and each "value" term is a quoted literal string. name specifies the name of the condition, such as feature or my_cfg.

When the cfg(...) option is specified, rustc will check every #[cfg(name = "value")] attribute, #[cfg_attr(name = "value")] attribute, #[link(name = "a", cfg(name = "value"))] attribute and cfg!(name = "value") macro call. It will check that the "value" specified is present in the list of expected values. If "value" is not in it, then rustc will report an unexpected_cfgs lint diagnostic. The default diagnostic level for this lint is Warn.

The command line --cfg arguments are currently NOT checked but may very well be checked in the future.

To enable checking of values, but to provide an none/empty set of expected values (ie. expect #[cfg(name)]), use these forms:

rustc --check-cfg 'cfg(name)'
rustc --check-cfg 'cfg(name, values())'

To enable checking of name but not values (i.e. unknown expected values), use this form:

rustc --check-cfg 'cfg(name, values(any()))'

To avoid repeating the same set of values, use this form:

rustc --check-cfg 'cfg(name1, ..., nameN, values("value1", "value2", ... "valueN"))'

The --check-cfg cfg(...) option can be repeated, both for the same condition name and for different names. If it is repeated for the same condition name, then the sets of values for that condition are merged together (precedence is given to values(any())).

Well known names and values

rustc has a internal list of well known names and their corresponding values. Those well known names and values follows the same stability as what they refer to.

Well known names and values checking is always enabled as long as at least one --check-cfg argument is present.

As of 2024-01-09T, the list of known names is as follows:

  • debug_assertions
  • doc
  • doctest
  • miri
  • overflow_checks
  • panic
  • proc_macro
  • relocation_model
  • sanitize
  • sanitizer_cfi_generalize_pointers
  • sanitizer_cfi_normalize_integers
  • target_abi
  • target_arch
  • target_endian
  • target_env
  • target_family
  • target_feature
  • target_has_atomic
  • target_has_atomic_equal_alignment
  • target_has_atomic_load_store
  • target_os
  • target_pointer_width
  • target_thread_local
  • target_vendor
  • test
  • unix
  • windows

Like with values(any()), well known names checking can be disabled by passing cfg(any()) as argument to --check-cfg.

Examples

Equivalence table

This table describe the equivalence of a --cfg argument to a --check-cfg argument.

--cfg--check-cfg
nothingnothing or --check-cfg=cfg() (to enable the checking)
--cfg foo--check-cfg=cfg(foo) or --check-cfg=cfg(foo, values())
--cfg foo=""--check-cfg=cfg(foo, values(""))
--cfg foo="bar"--check-cfg=cfg(foo, values("bar"))
--cfg foo="1" --cfg foo="2"--check-cfg=cfg(foo, values("1", "2"))
--cfg foo="1" --cfg bar="2"--check-cfg=cfg(foo, values("1")) --check-cfg=cfg(bar, values("2"))
--cfg foo --cfg foo="bar"--check-cfg=cfg(foo) --check-cfg=cfg(foo, values("bar"))

NOTE: There is (currently) no way to express that a condition name is expected but no (!= none) values are expected. Passing an empty values() means (none) in the sense of #[cfg(foo)] with no value. Users are expected to NOT pass a --check-cfg with that condition name.

Example: Cargo-like feature example

Consider this command line:

rustc --check-cfg 'cfg(feature, values("lion", "zebra"))' \
      --cfg 'feature="lion"' -Z unstable-options example.rs

This command line indicates that this crate has two features: lion and zebra. The lion feature is enabled, while the zebra feature is disabled. Given the --check-cfg arguments, exhaustive checking of names and values are enabled.

example.rs:

#![allow(unused)]
fn main() {
#[cfg(feature = "lion")]     // This condition is expected, as "lion" is an expected value of `feature`
fn tame_lion(lion: Lion) {}

#[cfg(feature = "zebra")]    // This condition is expected, as "zebra" is an expected value of `feature`
                             // but the condition will still evaluate to false
                             // since only --cfg feature="lion" was passed
fn ride_zebra(z: Zebra) {}

#[cfg(feature = "platypus")] // This condition is UNEXPECTED, as "platypus" is NOT an expected value of
                             // `feature` and will cause a compiler warning (by default).
fn poke_platypus() {}

#[cfg(feechure = "lion")]    // This condition is UNEXPECTED, as 'feechure' is NOT a expected condition
                             // name, no `cfg(feechure, ...)` was passed in `--check-cfg`
fn tame_lion() {}

#[cfg(windows = "unix")]     // This condition is UNEXPECTED, as while 'windows' is a well known
                             // condition name, it doens't expect any values
fn tame_windows() {}
}

Example: Multiple names and values

rustc --check-cfg 'cfg(is_embedded, has_feathers)' \
      --check-cfg 'cfg(feature, values("zapping", "lasers"))' \
      --cfg has_feathers --cfg 'feature="zapping"' -Z unstable-options
#![allow(unused)]
fn main() {
#[cfg(is_embedded)]         // This condition is expected, as 'is_embedded' was provided in --check-cfg
fn do_embedded() {}         // and doesn't take any value

#[cfg(has_feathers)]        // This condition is expected, as 'has_feathers' was provided in --check-cfg
fn do_features() {}         // and doesn't take any value

#[cfg(has_mumble_frotz)]    // This condition is UNEXPECTED, as 'has_mumble_frotz' was NEVER provided
                            // in any --check-cfg arguments
fn do_mumble_frotz() {}

#[cfg(feature = "lasers")]  // This condition is expected, as "lasers" is an expected value of `feature`
fn shoot_lasers() {}

#[cfg(feature = "monkeys")] // This condition is UNEXPECTED, as "monkeys" is NOT an expected value of
                            // `feature`
fn write_shakespeare() {}
}

Example: Condition names without values

rustc --check-cfg 'cfg(is_embedded, has_feathers, values(any()))' \
      --cfg has_feathers -Z unstable-options
#![allow(unused)]
fn main() {
#[cfg(is_embedded)]      // This condition is expected, as 'is_embedded' was provided in --check-cfg
                         // as condition name
fn do_embedded() {}

#[cfg(has_feathers)]     // This condition is expected, as "has_feathers" was provided in --check-cfg
                         // as condition name
fn do_features() {}

#[cfg(has_feathers = "zapping")] // This condition is expected, as "has_feathers" was provided in
                                 // and because *any* values is expected for 'has_feathers' no
                                 // warning is emitted for the value "zapping"
fn do_zapping() {}

#[cfg(has_mumble_frotz)] // This condition is UNEXPECTED, as 'has_mumble_frotz' was not provided
                         // in any --check-cfg arguments
fn do_mumble_frotz() {}
}