Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement RFC3695 Allow boolean literals as cfg predicates #131034

Merged
merged 5 commits into from
Oct 4, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions compiler/rustc_ast/src/attr/mod.rs
Original file line number Diff line number Diff line change
@@ -527,6 +527,16 @@ impl NestedMetaItem {
}
}

/// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem` or if it's
/// `NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(_), .. })`.
pub fn meta_item_or_bool(&self) -> Option<&NestedMetaItem> {
match self {
NestedMetaItem::MetaItem(_item) => Some(self),
NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(_), .. }) => Some(self),
_ => None,
}
}

/// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
pub fn meta_item(&self) -> Option<&MetaItem> {
match self {
2 changes: 2 additions & 0 deletions compiler/rustc_attr/messages.ftl
Original file line number Diff line number Diff line change
@@ -107,6 +107,8 @@ attr_unknown_version_literal =
attr_unstable_cfg_target_compact =
compact `cfg(target(..))` is experimental and subject to change

attr_unsupported_literal_cfg_boolean =
literal in `cfg` predicate value must be a boolean
attr_unsupported_literal_cfg_string =
literal in `cfg` predicate value must be a string
attr_unsupported_literal_deprecated_kv_pair =
57 changes: 45 additions & 12 deletions compiler/rustc_attr/src/builtin.rs
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ use rustc_session::parse::feature_err;
use rustc_session::{RustcVersion, Session};
use rustc_span::Span;
use rustc_span::hygiene::Transparency;
use rustc_span::symbol::{Symbol, sym};
use rustc_span::symbol::{Symbol, kw, sym};

use crate::fluent_generated;
use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
@@ -36,6 +36,7 @@ pub fn is_builtin_attr(attr: &Attribute) -> bool {
pub(crate) enum UnsupportedLiteralReason {
Generic,
CfgString,
CfgBoolean,
DeprecatedString,
DeprecatedKvPair,
}
@@ -523,7 +524,7 @@ pub struct Condition {

/// Tests if a cfg-pattern matches the cfg set
pub fn cfg_matches(
cfg: &ast::MetaItem,
cfg: &ast::NestedMetaItem,
sess: &Session,
lint_node_id: NodeId,
features: Option<&Features>,
@@ -594,12 +595,43 @@ pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
/// evaluate individual items.
pub fn eval_condition(
cfg: &ast::MetaItem,
cfg: &ast::NestedMetaItem,
sess: &Session,
features: Option<&Features>,
eval: &mut impl FnMut(Condition) -> bool,
) -> bool {
let dcx = sess.dcx();

let cfg = match cfg {
ast::NestedMetaItem::MetaItem(meta_item) => meta_item,
ast::NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
if let Some(features) = features {
// we can't use `try_gate_cfg` as symbols don't differentiate between `r#true`
// and `true`, and we want to keep the former working without feature gate
gate_cfg(
&((
if *b { kw::True } else { kw::False },
sym::cfg_boolean_literals,
|features: &Features| features.cfg_boolean_literals,
)),
cfg.span(),
sess,
features,
);
}
return *b;
}
_ => {
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
span: cfg.span(),
reason: UnsupportedLiteralReason::CfgBoolean,
is_bytestr: false,
start_point_span: sess.source_map().start_point(cfg.span()),
});
return false;
}
};

match &cfg.kind {
ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
try_gate_cfg(sym::version, cfg.span, sess, features);
@@ -635,7 +667,7 @@ pub fn eval_condition(
}
ast::MetaItemKind::List(mis) => {
for mi in mis.iter() {
if !mi.is_meta_item() {
if mi.meta_item_or_bool().is_none() {
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
span: mi.span(),
reason: UnsupportedLiteralReason::Generic,
@@ -653,23 +685,19 @@ pub fn eval_condition(
.iter()
// We don't use any() here, because we want to evaluate all cfg condition
// as eval_condition can (and does) extra checks
.fold(false, |res, mi| {
res | eval_condition(mi.meta_item().unwrap(), sess, features, eval)
}),
.fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)),
sym::all => mis
.iter()
// We don't use all() here, because we want to evaluate all cfg condition
// as eval_condition can (and does) extra checks
.fold(true, |res, mi| {
res & eval_condition(mi.meta_item().unwrap(), sess, features, eval)
}),
.fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)),
sym::not => {
let [mi] = mis.as_slice() else {
dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span });
return false;
};

!eval_condition(mi.meta_item().unwrap(), sess, features, eval)
!eval_condition(mi, sess, features, eval)
}
sym::target => {
if let Some(features) = features
@@ -690,7 +718,12 @@ pub fn eval_condition(
seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
}

res & eval_condition(&mi, sess, features, eval)
res & eval_condition(
&ast::NestedMetaItem::MetaItem(mi),
sess,
features,
eval,
)
})
}
_ => {
1 change: 1 addition & 0 deletions compiler/rustc_attr/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
@@ -206,6 +206,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral {
let mut diag = Diag::new(dcx, level, match self.reason {
UnsupportedLiteralReason::Generic => fluent::attr_unsupported_literal_generic,
UnsupportedLiteralReason::CfgString => fluent::attr_unsupported_literal_cfg_string,
UnsupportedLiteralReason::CfgBoolean => fluent::attr_unsupported_literal_cfg_boolean,
UnsupportedLiteralReason::DeprecatedString => {
fluent::attr_unsupported_literal_deprecated_string
}
9 changes: 6 additions & 3 deletions compiler/rustc_builtin_macros/src/cfg.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@ use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::PResult;
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
use rustc_parse::parser::attr::AllowLeadingUnsafe;
use rustc_span::Span;
use {rustc_ast as ast, rustc_attr as attr};

@@ -36,14 +35,18 @@ pub(crate) fn expand_cfg(
})
}

fn parse_cfg<'a>(cx: &ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> {
fn parse_cfg<'a>(
cx: &ExtCtxt<'a>,
span: Span,
tts: TokenStream,
) -> PResult<'a, ast::NestedMetaItem> {
let mut p = cx.new_parser_from_tts(tts);

if p.token == token::Eof {
return Err(cx.dcx().create_err(errors::RequiresCfgPattern { span }));
}

let cfg = p.parse_meta_item(AllowLeadingUnsafe::No)?;
let cfg = p.parse_meta_item_inner()?;

let _ = p.eat(&token::Comma);

2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
@@ -156,7 +156,7 @@ pub struct NativeLib {
pub kind: NativeLibKind,
pub name: Symbol,
pub filename: Option<Symbol>,
pub cfg: Option<ast::MetaItem>,
pub cfg: Option<ast::NestedMetaItem>,
pub verbatim: bool,
pub dll_imports: Vec<cstore::DllImport>,
}
8 changes: 5 additions & 3 deletions compiler/rustc_expand/src/config.rs
Original file line number Diff line number Diff line change
@@ -5,7 +5,9 @@ use rustc_ast::token::{Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{
AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree,
};
use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, NodeId};
use rustc_ast::{
self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, NestedMetaItem, NodeId,
};
use rustc_attr as attr;
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
use rustc_feature::{
@@ -449,7 +451,7 @@ impl<'a> StripUnconfigured<'a> {
}
}

pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItem> {
pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a NestedMetaItem> {
let span = meta_item.span;
match meta_item.meta_item_list() {
None => {
@@ -464,7 +466,7 @@ pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a Meta
sess.dcx().emit_err(InvalidCfg::MultiplePredicates { span: l.span() });
None
}
Some([single]) => match single.meta_item() {
Some([single]) => match single.meta_item_or_bool() {
Some(meta_item) => Some(meta_item),
None => {
sess.dcx().emit_err(InvalidCfg::PredicateLiteral { span: single.span() });
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
@@ -371,6 +371,8 @@ declare_features! (
(unstable, async_for_loop, "1.77.0", Some(118898)),
/// Allows using C-variadics.
(unstable, c_variadic, "1.34.0", Some(44930)),
/// Allows the use of `#[cfg(<true/false>)]`.
(unstable, cfg_boolean_literals, "CURRENT_RUSTC_VERSION", Some(131204)),
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
(unstable, cfg_overflow_checks, "1.71.0", Some(111466)),
/// Provides the relocation model information as cfg entry
9 changes: 7 additions & 2 deletions compiler/rustc_metadata/src/native_libs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::ops::ControlFlow;
use std::path::{Path, PathBuf};

use rustc_ast::{CRATE_NODE_ID, NestedMetaItem};
use rustc_ast::CRATE_NODE_ID;
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::query::LocalCrate;
@@ -304,7 +304,12 @@ impl<'tcx> Collector<'tcx> {
sess.dcx().emit_err(errors::LinkCfgForm { span: item.span() });
continue;
};
let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else {
let [link_cfg] = link_cfg else {
sess.dcx()
.emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
continue;
};
let Some(link_cfg) = link_cfg.meta_item_or_bool() else {
sess.dcx()
.emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
continue;
4 changes: 2 additions & 2 deletions compiler/rustc_parse/src/lib.rs
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ use std::path::Path;

use rustc_ast as ast;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrItem, Attribute, MetaItem, token};
use rustc_ast::{AttrItem, Attribute, NestedMetaItem, token};
use rustc_ast_pretty::pprust;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Diag, FatalError, PResult};
@@ -160,7 +160,7 @@ pub fn fake_token_stream_for_crate(psess: &ParseSess, krate: &ast::Crate) -> Tok
pub fn parse_cfg_attr(
cfg_attr: &Attribute,
psess: &ParseSess,
) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
) -> Option<(NestedMetaItem, Vec<(AttrItem, Span)>)> {
const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]";
const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
<https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>";
8 changes: 5 additions & 3 deletions compiler/rustc_parse/src/parser/attr.rs
Original file line number Diff line number Diff line change
@@ -356,8 +356,10 @@ impl<'a> Parser<'a> {
}

/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> {
let cfg_predicate = self.parse_meta_item(AllowLeadingUnsafe::No)?;
pub fn parse_cfg_attr(
&mut self,
) -> PResult<'a, (ast::NestedMetaItem, Vec<(ast::AttrItem, Span)>)> {
let cfg_predicate = self.parse_meta_item_inner()?;
self.expect(&token::Comma)?;

// Presumably, the majority of the time there will only be one attr.
@@ -452,7 +454,7 @@ impl<'a> Parser<'a> {
/// ```ebnf
/// MetaItemInner = UNSUFFIXED_LIT | MetaItem ;
/// ```
fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
pub fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
match self.parse_unsuffixed_meta_item_lit() {
Ok(lit) => return Ok(ast::NestedMetaItem::Lit(lit)),
Err(err) => err.cancel(), // we provide a better error below
2 changes: 1 addition & 1 deletion compiler/rustc_session/src/cstore.rs
Original file line number Diff line number Diff line change
@@ -72,7 +72,7 @@ pub struct NativeLib {
pub name: Symbol,
/// If packed_bundled_libs enabled, actual filename of library is stored.
pub filename: Option<Symbol>,
pub cfg: Option<ast::MetaItem>,
pub cfg: Option<ast::NestedMetaItem>,
pub foreign_module: Option<DefId>,
pub verbatim: Option<bool>,
pub dll_imports: Vec<DllImport>,
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -543,6 +543,7 @@ symbols! {
cfg_accessible,
cfg_attr,
cfg_attr_multi,
cfg_boolean_literals,
cfg_doctest,
cfg_eval,
cfg_fmt_debug,
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::iter;
use std::path::PathBuf;

use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, MetaItem, NestedMetaItem};
use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, NestedMetaItem};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::codes::*;
use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
@@ -282,7 +282,7 @@ pub struct OnUnimplementedFormatString {

#[derive(Debug)]
pub struct OnUnimplementedDirective {
pub condition: Option<MetaItem>,
pub condition: Option<NestedMetaItem>,
pub subcommands: Vec<OnUnimplementedDirective>,
pub message: Option<OnUnimplementedFormatString>,
pub label: Option<OnUnimplementedFormatString>,
@@ -414,7 +414,7 @@ impl<'tcx> OnUnimplementedDirective {
let cond = item_iter
.next()
.ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))?
.meta_item()
.meta_item_or_bool()
.ok_or_else(|| tcx.dcx().emit_err(InvalidOnClauseInOnUnimplemented { span }))?;
attr::eval_condition(cond, &tcx.sess, Some(tcx.features()), &mut |cfg| {
if let Some(value) = cfg.value
@@ -558,8 +558,8 @@ impl<'tcx> OnUnimplementedDirective {
IgnoredDiagnosticOption::maybe_emit_warning(
tcx,
item_def_id,
directive.condition.as_ref().map(|i| i.span),
aggr.condition.as_ref().map(|i| i.span),
directive.condition.as_ref().map(|i| i.span()),
aggr.condition.as_ref().map(|i| i.span()),
"condition",
);
IgnoredDiagnosticOption::maybe_emit_warning(
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# `cfg_boolean_literals`

The tracking issue for this feature is: [#131204]

[#131204]: https://github.com/rust-lang/rust/issues/131204

------------------------

The `cfg_boolean_literals` feature makes it possible to use the `true`/`false`
literal as cfg predicate. They always evaluate to true/false respectively.

## Examples

```rust
#![feature(cfg_boolean_literals)]

#[cfg(true)]
const A: i32 = 5;

#[cfg(all(false))]
const A: i32 = 58 * 89;
```
10 changes: 7 additions & 3 deletions src/librustdoc/clean/cfg.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
use std::fmt::{self, Write};
use std::{mem, ops};

use rustc_ast::{LitKind, MetaItem, MetaItemKind, NestedMetaItem};
use rustc_ast::{LitKind, MetaItem, MetaItemKind, MetaItemLit, NestedMetaItem};
use rustc_data_structures::fx::FxHashSet;
use rustc_feature::Features;
use rustc_session::parse::ParseSess;
@@ -48,6 +48,10 @@ impl Cfg {
) -> Result<Option<Cfg>, InvalidCfgError> {
match nested_cfg {
NestedMetaItem::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude),
NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => match *b {
true => Ok(Some(Cfg::True)),
false => Ok(Some(Cfg::False)),
},
NestedMetaItem::Lit(ref lit) => {
Err(InvalidCfgError { msg: "unexpected literal", span: lit.span })
}
@@ -120,8 +124,8 @@ impl Cfg {
///
/// If the content is not properly formatted, it will return an error indicating what and where
/// the error is.
pub(crate) fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
Self::parse_without(cfg, &FxHashSet::default()).map(|ret| ret.unwrap())
pub(crate) fn parse(cfg: &NestedMetaItem) -> Result<Cfg, InvalidCfgError> {
Self::parse_nested(cfg, &FxHashSet::default()).map(|ret| ret.unwrap())
}

/// Checks whether the given configuration can be matched in the current session.
51 changes: 34 additions & 17 deletions src/librustdoc/clean/cfg/tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rustc_ast::{MetaItemLit, Path, Safety, StrStyle};
use rustc_ast::ast::LitIntType;
use rustc_ast::{MetaItemLit, NestedMetaItem, Path, Safety, StrStyle};
use rustc_span::symbol::{Ident, kw};
use rustc_span::{DUMMY_SP, create_default_session_globals_then};
use thin_vec::thin_vec;
@@ -13,52 +14,52 @@ fn name_value_cfg(name: &str, value: &str) -> Cfg {
Cfg::Cfg(Symbol::intern(name), Some(Symbol::intern(value)))
}

fn dummy_meta_item_word(name: &str) -> MetaItem {
MetaItem {
fn dummy_lit(symbol: Symbol, kind: LitKind) -> NestedMetaItem {
NestedMetaItem::Lit(MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP })
}

fn dummy_meta_item_word(name: &str) -> NestedMetaItem {
NestedMetaItem::MetaItem(MetaItem {
unsafety: Safety::Default,
path: Path::from_ident(Ident::from_str(name)),
kind: MetaItemKind::Word,
span: DUMMY_SP,
}
})
}

fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItem {
fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> NestedMetaItem {
let lit = MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP };
MetaItem {
NestedMetaItem::MetaItem(MetaItem {
unsafety: Safety::Default,
path: Path::from_ident(Ident::from_str(name)),
kind: MetaItemKind::NameValue(lit),
span: DUMMY_SP,
}
})
}

macro_rules! dummy_meta_item_list {
($name:ident, [$($list:ident),* $(,)?]) => {
MetaItem {
NestedMetaItem::MetaItem(MetaItem {
unsafety: Safety::Default,
path: Path::from_ident(Ident::from_str(stringify!($name))),
kind: MetaItemKind::List(thin_vec![
$(
NestedMetaItem::MetaItem(
dummy_meta_item_word(stringify!($list)),
),
dummy_meta_item_word(stringify!($list)),
)*
]),
span: DUMMY_SP,
}
})
};

($name:ident, [$($list:expr),* $(,)?]) => {
MetaItem {
NestedMetaItem::MetaItem(MetaItem {
unsafety: Safety::Default,
path: Path::from_ident(Ident::from_str(stringify!($name))),
kind: MetaItemKind::List(thin_vec![
$(
NestedMetaItem::MetaItem($list),
)*
$($list,)*
]),
span: DUMMY_SP,
}
})
};
}

@@ -251,6 +252,14 @@ fn test_cfg_or() {
#[test]
fn test_parse_ok() {
create_default_session_globals_then(|| {
let r#true = Symbol::intern("true");
let mi = dummy_lit(r#true, LitKind::Bool(true));
assert_eq!(Cfg::parse(&mi), Ok(Cfg::True));

let r#false = Symbol::intern("false");
let mi = dummy_lit(r#false, LitKind::Bool(false));
assert_eq!(Cfg::parse(&mi), Ok(Cfg::False));

let mi = dummy_meta_item_word("all");
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all")));

@@ -309,6 +318,14 @@ fn test_parse_err() {

let mi = dummy_meta_item_list!(not, [dummy_meta_item_list!(foo, []),]);
assert!(Cfg::parse(&mi).is_err());

let c = Symbol::intern("e");
let mi = dummy_lit(c, LitKind::Char('e'));
assert!(Cfg::parse(&mi).is_err());

let five = Symbol::intern("5");
let mi = dummy_lit(five, LitKind::Int(5.into(), LitIntType::Unsuffixed));
assert!(Cfg::parse(&mi).is_err());
})
}

5 changes: 3 additions & 2 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ use std::sync::{Arc, OnceLock as OnceCell};
use std::{fmt, iter};

use arrayvec::ArrayVec;
use rustc_ast::NestedMetaItem;
use rustc_ast_pretty::pprust;
use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel, StableSince};
use rustc_const_eval::const_eval::is_unstable_const_fn;
@@ -1016,7 +1017,7 @@ pub(crate) trait AttributesExt {
.peekable();
if doc_cfg.peek().is_some() && doc_cfg_active {
doc_cfg
.filter_map(|attr| Cfg::parse(attr.meta_item()?).ok())
.filter_map(|attr| Cfg::parse(&attr).ok())
.fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
} else if doc_auto_cfg_active {
// If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because
@@ -1072,7 +1073,7 @@ pub(crate) trait AttributesExt {
let mut meta = attr.meta_item().unwrap().clone();
meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature));

if let Ok(feat_cfg) = Cfg::parse(&meta) {
if let Ok(feat_cfg) = Cfg::parse(&NestedMetaItem::MetaItem(meta)) {
cfg &= feat_cfg;
}
}
2 changes: 1 addition & 1 deletion src/librustdoc/visit_ast.rs
Original file line number Diff line number Diff line change
@@ -164,7 +164,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
.unwrap_or(&[])
.iter()
.filter_map(|attr| {
Cfg::parse(attr.meta_item()?)
Cfg::parse(attr)
.map_err(|e| self.cx.sess().dcx().span_err(e.span, e.msg))
.ok()
})
19 changes: 19 additions & 0 deletions tests/rustdoc-ui/cfg-boolean-literal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//@ check-pass

#![feature(cfg_boolean_literals)]
#![feature(doc_cfg)]

#[doc(cfg(false))]
pub fn foo() {}

#[doc(cfg(true))]
pub fn bar() {}

#[doc(cfg(any(true)))]
pub fn zoo() {}

#[doc(cfg(all(true)))]
pub fn toy() {}

#[doc(cfg(not(true)))]
pub fn nay() {}
19 changes: 19 additions & 0 deletions tests/ui/cfg/raw-true-false.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//@ check-pass
//@ compile-flags: --cfg false --check-cfg=cfg(r#false)

#![deny(warnings)]

#[expect(unexpected_cfgs)]
mod a {
#[cfg(r#true)]
pub fn foo() {}
}

mod b {
#[cfg(r#false)]
pub fn bar() {}
}

fn main() {
b::bar()
}
30 changes: 30 additions & 0 deletions tests/ui/cfg/true-false.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//@ run-pass

#![feature(link_cfg)]
#![feature(cfg_boolean_literals)]

#[cfg(true)]
fn foo() -> bool {
cfg!(true)
}

#[cfg(false)]
fn foo() -> bool {
cfg!(false)
}

#[cfg_attr(true, cfg(false))]
fn foo() {}

#[link(name = "foo", cfg(false))]
extern "C" {}

fn main() {
assert!(foo());
assert!(cfg!(true));
assert!(!cfg!(false));
assert!(cfg!(not(false)));
assert!(cfg!(all(true)));
assert!(cfg!(any(true)));
assert!(!cfg!(not(true)));
}
10 changes: 10 additions & 0 deletions tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[cfg(true)] //~ ERROR `cfg(true)` is experimental
fn foo() {}

#[cfg_attr(true, cfg(false))] //~ ERROR `cfg(true)` is experimental
//~^ ERROR `cfg(false)` is experimental
fn foo() {}

fn main() {
cfg!(false); //~ ERROR `cfg(false)` is experimental
}
43 changes: 43 additions & 0 deletions tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
error[E0658]: `cfg(true)` is experimental and subject to change
--> $DIR/feature-gate-cfg-boolean-literals.rs:1:7
|
LL | #[cfg(true)]
| ^^^^
|
= note: see issue #131204 <https://github.com/rust-lang/rust/issues/131204> for more information
= help: add `#![feature(cfg_boolean_literals)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0658]: `cfg(true)` is experimental and subject to change
--> $DIR/feature-gate-cfg-boolean-literals.rs:4:12
|
LL | #[cfg_attr(true, cfg(false))]
| ^^^^
|
= note: see issue #131204 <https://github.com/rust-lang/rust/issues/131204> for more information
= help: add `#![feature(cfg_boolean_literals)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0658]: `cfg(false)` is experimental and subject to change
--> $DIR/feature-gate-cfg-boolean-literals.rs:4:22
|
LL | #[cfg_attr(true, cfg(false))]
| ^^^^^
|
= note: see issue #131204 <https://github.com/rust-lang/rust/issues/131204> for more information
= help: add `#![feature(cfg_boolean_literals)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0658]: `cfg(false)` is experimental and subject to change
--> $DIR/feature-gate-cfg-boolean-literals.rs:9:10
|
LL | cfg!(false);
| ^^^^^
|
= note: see issue #131204 <https://github.com/rust-lang/rust/issues/131204> for more information
= help: add `#![feature(cfg_boolean_literals)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0658`.
2 changes: 1 addition & 1 deletion tests/ui/macros/cfg.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
fn main() {
cfg!(); //~ ERROR macro requires a cfg-pattern
cfg!(123); //~ ERROR expected identifier
cfg!(123); //~ ERROR literal in `cfg` predicate value must be a boolean
cfg!(foo = 123); //~ ERROR literal in `cfg` predicate value must be a string
cfg!(foo, bar); //~ ERROR expected 1 cfg-pattern
}
4 changes: 2 additions & 2 deletions tests/ui/macros/cfg.stderr
Original file line number Diff line number Diff line change
@@ -6,11 +6,11 @@ LL | cfg!();
|
= note: this error originates in the macro `cfg` (in Nightly builds, run with -Z macro-backtrace for more info)

error: expected identifier, found `123`
error[E0565]: literal in `cfg` predicate value must be a boolean
--> $DIR/cfg.rs:3:10
|
LL | cfg!(123);
| ^^^ expected identifier
| ^^^

error[E0565]: literal in `cfg` predicate value must be a string
--> $DIR/cfg.rs:4:16
Loading