#[macro_export]
#[allow_internal_unstable(stdarch_internal)]
#[unstable(feature = "stdarch_internal", issue = "none")]
macro_rules! detect_feature {
    ($feature:tt, $feature_lit:tt) => {
        $crate::detect_feature!($feature, $feature_lit : $feature_lit)
    };
    ($feature:tt, $feature_lit:tt : $($target_feature_lit:tt),*) => {
        $(cfg!(target_feature = $target_feature_lit) ||)*
            $crate::detect::__is_feature_detected::$feature()
    };
    ($feature:tt, $feature_lit:tt, without cfg check: true) => {
        $crate::detect::__is_feature_detected::$feature()
    };
}

#[allow(unused_macros, reason = "it's used in the features! macro below")]
macro_rules! check_cfg_feature {
    ($feature:tt, $feature_lit:tt) => {
        check_cfg_feature!($feature, $feature_lit : $feature_lit)
    };
    ($feature:tt, $feature_lit:tt : $($target_feature_lit:tt),*) => {
        $(cfg!(target_feature = $target_feature_lit);)*
    };
    ($feature:tt, $feature_lit:tt, without cfg check: $feature_cfg_check:literal) => {
        #[allow(unexpected_cfgs, reason = $feature_lit)]
        { cfg!(target_feature = $feature_lit) }
    };
}

#[allow(unused)]
macro_rules! features {
    (
      @TARGET: $target:ident;
      @CFG: $cfg:meta;
      @MACRO_NAME: $macro_name:ident;
      @MACRO_ATTRS: $(#[$macro_attrs:meta])*
      $(@BIND_FEATURE_NAME: $bind_feature:tt; $feature_impl:tt; $(#[$deprecate_attr:meta];)?)*
      $(@NO_RUNTIME_DETECTION: $nort_feature:tt; )*
      $(@FEATURE: #[$stability_attr:meta] $feature:ident: $feature_lit:tt;
          $(without cfg check: $feature_cfg_check:tt;)?
          $(implied by target_features: [$($target_feature_lit:tt),*];)?
          $(#[$feature_comment:meta])*)*
    ) => {
        #[macro_export]
        $(#[$macro_attrs])*
        #[allow_internal_unstable(stdarch_internal)]
        #[cfg($cfg)]
        #[doc(cfg($cfg))]
        macro_rules! $macro_name {
            $(
                ($feature_lit) => {
                    $crate::detect_feature!($feature, $feature_lit $(, without cfg check: $feature_cfg_check)? $(: $($target_feature_lit),*)?)
                };
            )*
            $(
                ($bind_feature) => {
                    {
                        $(
                            #[$deprecate_attr] macro_rules! deprecated_feature { {} => {}; }
                            deprecated_feature! {};
                        )?
                        $crate::$macro_name!($feature_impl)
                    }
                };
            )*
            $(
                ($nort_feature) => {
                    compile_error!(
                        concat!(
                            stringify!($nort_feature),
                            " feature cannot be detected at run-time"
                        )
                    )
                };
            )*
            ($t:tt,) => {
                    $crate::$macro_name!($t);
            };
            ($t:tt) => {
                compile_error!(
                    concat!(
                        concat!("unknown ", stringify!($target)),
                        concat!(" target feature: ", $t)
                    )
                )
            };
        }

        $(#[$macro_attrs])*
        #[macro_export]
        #[cfg(not($cfg))]
        #[doc(cfg($cfg))]
        macro_rules! $macro_name {
            $(
                ($feature_lit) => {
                    compile_error!(
                        concat!(
                            r#"This macro cannot be used on the current target.
                            You can prevent it from being used in other architectures by
                            guarding it behind a cfg("#,
                            stringify!($cfg),
                            ")."
                        )
                    )
                };
            )*
            $(
                ($bind_feature) => { $crate::$macro_name!($feature_impl) };
            )*
            $(
                ($nort_feature) => {
                    compile_error!(
                        concat!(
                            stringify!($nort_feature),
                            " feature cannot be detected at run-time"
                        )
                    )
                };
            )*
            ($t:tt,) => {
                    $crate::$macro_name!($t);
            };
            ($t:tt) => {
                compile_error!(
                    concat!(
                        concat!("unknown ", stringify!($target)),
                        concat!(" target feature: ", $t)
                    )
                )
            };
        }

        #[deny(unexpected_cfgs)]
        #[deny(unfulfilled_lint_expectations)]
        const _: () = {
            $(
                check_cfg_feature!($feature, $feature_lit $(, without cfg check: $feature_cfg_check)? $(: $($target_feature_lit),*)?);
            )*
        };

        /// Each variant denotes a position in a bitset for a particular feature.
        ///
        /// PLEASE: do not use this, it is an implementation detail subject
        /// to change.
        #[doc(hidden)]
        #[allow(non_camel_case_types)]
        #[derive(Copy, Clone)]
        #[repr(u8)]
        #[unstable(feature = "stdarch_internal", issue = "none")]
        #[cfg($cfg)]
        pub(crate) enum Feature {
            $(
                $(#[$feature_comment])*
                $feature,
            )*

            // Do not add variants after last:
            _last
        }

        #[cfg($cfg)]
        impl Feature {
            pub(crate) fn to_str(self) -> &'static str {
                match self {
                    $(Feature::$feature => $feature_lit,)*
                    Feature::_last => unreachable!(),
                }
            }

            #[cfg(feature = "std_detect_env_override")]
            pub(crate) fn from_str(s: &str) -> Result<Feature, ()> {
                match s {
                    $($feature_lit => Ok(Feature::$feature),)*
                    _ => Err(())
                }
            }
        }

        /// Each function performs run-time feature detection for a single
        /// feature. This allow us to use stability attributes on a per feature
        /// basis.
        ///
        /// PLEASE: do not use this, it is an implementation detail subject
        /// to change.
        #[doc(hidden)]
        #[cfg($cfg)]
        #[unstable(feature = "stdarch_internal", issue = "none")]
        pub mod __is_feature_detected {
            $(

                /// PLEASE: do not use this, it is an implementation detail
                /// subject to change.
                #[inline]
                #[doc(hidden)]
                #[$stability_attr]
                pub fn $feature() -> bool {
                    $crate::detect::check_for($crate::detect::Feature::$feature)
                }
            )*
        }
    };
}
