Skip to content
Draft
Show file tree
Hide file tree
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
67 changes: 32 additions & 35 deletions compiler/rustc_attr_parsing/src/attributes/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_session::config::ExpectedValues;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::parse::{ParseSess, feature_err};
use rustc_span::{Span, Symbol, sym};
use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
use thin_vec::ThinVec;

use crate::context::{AcceptContext, ShouldEmit, Stage};
Expand Down Expand Up @@ -47,20 +47,19 @@ pub fn parse_cfg<'c, S: Stage>(
cx.expected_single_argument(list.span);
return None;
};
parse_cfg_entry(cx, single)
parse_cfg_entry(cx, single).ok()
}

pub(crate) fn parse_cfg_entry<S: Stage>(
pub fn parse_cfg_entry<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
item: &MetaItemOrLitParser<'_>,
) -> Option<CfgEntry> {
Some(match item {
) -> Result<CfgEntry, ErrorGuaranteed> {
Ok(match item {
MetaItemOrLitParser::MetaItemParser(meta) => match meta.args() {
ArgParser::List(list) => match meta.path().word_sym() {
Some(sym::not) => {
let Some(single) = list.single() else {
cx.expected_single_argument(list.span);
return None;
return Err(cx.expected_single_argument(list.span));
};
CfgEntry::Not(Box::new(parse_cfg_entry(cx, single)?), list.span)
}
Expand All @@ -75,49 +74,47 @@ pub(crate) fn parse_cfg_entry<S: Stage>(
Some(sym::target) => parse_cfg_entry_target(cx, list, meta.span())?,
Some(sym::version) => parse_cfg_entry_version(cx, list, meta.span())?,
_ => {
cx.emit_err(session_diagnostics::InvalidPredicate {
return Err(cx.emit_err(session_diagnostics::InvalidPredicate {
span: meta.span(),
predicate: meta.path().to_string(),
});
return None;
}));
}
},
a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
let Some(name) = meta.path().word_sym() else {
cx.expected_identifier(meta.path().span());
return None;
return Err(cx.expected_identifier(meta.path().span()));
};
parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)?
}
},
MetaItemOrLitParser::Lit(lit) => match lit.kind {
LitKind::Bool(b) => CfgEntry::Bool(b, lit.span),
_ => {
cx.expected_identifier(lit.span);
return None;
}
_ => return Err(cx.expected_identifier(lit.span)),
},
MetaItemOrLitParser::Err(_, _) => return None,
MetaItemOrLitParser::Err(_, err) => return Err(*err),
})
}

fn parse_cfg_entry_version<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
list: &MetaItemListParser<'_>,
meta_span: Span,
) -> Option<CfgEntry> {
) -> Result<CfgEntry, ErrorGuaranteed> {
try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option());
let Some(version) = list.single() else {
cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span });
return None;
return Err(
cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span })
);
};
let Some(version_lit) = version.lit() else {
cx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: version.span() });
return None;
return Err(
cx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: version.span() })
);
};
let Some(version_str) = version_lit.value_str() else {
cx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: version_lit.span });
return None;
return Err(
cx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: version_lit.span })
);
};

let min_version = parse_version(version_str).or_else(|| {
Expand All @@ -127,14 +124,14 @@ fn parse_cfg_entry_version<S: Stage>(
None
});

Some(CfgEntry::Version(min_version, list.span))
Ok(CfgEntry::Version(min_version, list.span))
}

fn parse_cfg_entry_target<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
list: &MetaItemListParser<'_>,
meta_span: Span,
) -> Option<CfgEntry> {
) -> Result<CfgEntry, ErrorGuaranteed> {
if let Some(features) = cx.features_option()
&& !features.cfg_target_compact()
{
Expand All @@ -161,17 +158,16 @@ fn parse_cfg_entry_target<S: Stage>(

// Then, parse it as a name-value item
let Some(name) = sub_item.path().word_sym() else {
cx.expected_identifier(sub_item.path().span());
return None;
return Err(cx.expected_identifier(sub_item.path().span()));
};
let name = Symbol::intern(&format!("target_{name}"));
if let Some(cfg) =
if let Ok(cfg) =
parse_name_value(name, sub_item.path().span(), Some(nv), sub_item.span(), cx)
{
result.push(cfg);
}
}
Some(CfgEntry::All(result, list.span))
Ok(CfgEntry::All(result, list.span))
}

fn parse_name_value<S: Stage>(
Expand All @@ -180,21 +176,22 @@ fn parse_name_value<S: Stage>(
value: Option<&NameValueParser>,
span: Span,
cx: &mut AcceptContext<'_, '_, S>,
) -> Option<CfgEntry> {
) -> Result<CfgEntry, ErrorGuaranteed> {
try_gate_cfg(name, span, cx.sess(), cx.features_option());

let value = match value {
None => None,
Some(value) => {
let Some(value_str) = value.value_as_str() else {
cx.expected_string_literal(value.value_span, Some(value.value_as_lit()));
return None;
return Err(
cx.expected_string_literal(value.value_span, Some(value.value_as_lit()))
);
};
Some((value_str, value.value_span))
}
};

Some(CfgEntry::NameValue { name, name_span, value, span })
Ok(CfgEntry::NameValue { name, name_span, value, span })
}

pub fn eval_config_entry(
Expand Down Expand Up @@ -404,7 +401,7 @@ fn parse_cfg_attr_internal<'a>(
parse_cfg_entry,
&CFG_ATTR_TEMPLATE,
)
.ok_or_else(|| {
.map_err(|_err: ErrorGuaranteed| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the delayed bug below necessary? Having an error guaranteed here effectively proves error emission already

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this is in a Parser run, we need to produce PResult, which is Result<T, Diag>. This was my hack to create a Diag, I was still gonna see if there was a better way to do this

let mut diag = sess.dcx().struct_err(
"cfg_entry parsing failing with `ShouldEmit::ErrorsAndLints` should emit a error.",
);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ impl LinkParser {
)
.emit();
}
*cfg = parse_cfg_entry(cx, link_cfg);
*cfg = parse_cfg_entry(cx, link_cfg).ok();
true
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_attr_parsing/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,9 @@ impl<'sess> AttributeParser<'sess, Early> {
features: Option<&'sess Features>,
emit_errors: ShouldEmit,
args: &I,
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &I) -> Option<T>,
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &I) -> T,
template: &AttributeTemplate,
) -> Option<T> {
) -> T {
let mut parser = Self {
features,
tools: Vec::new(),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ mod target_checking;
pub mod validate_attr;

pub use attributes::cfg::{
CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg, parse_cfg_attr,
CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg, parse_cfg_attr, parse_cfg_entry,
};
pub use attributes::cfg_old::*;
pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
Expand Down
60 changes: 36 additions & 24 deletions compiler/rustc_builtin_macros/src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
//! a literal `true` or `false` based on whether the given cfg matches the
//! current compilation environment.

use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::PResult;
use rustc_ast::{AttrStyle, CRATE_NODE_ID, token};
use rustc_attr_parsing as attr;
use rustc_attr_parsing::parser::MetaItemOrLitParser;
use rustc_attr_parsing::{AttributeParser, CFG_TEMPLATE, ShouldEmit, parse_cfg_entry};
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
use rustc_hir::AttrPath;
use rustc_hir::attrs::CfgEntry;
use rustc_parse::exp;
use rustc_span::Span;
use {rustc_ast as ast, rustc_attr_parsing as attr};
use rustc_span::{ErrorGuaranteed, Ident, Span};

use crate::errors;

Expand All @@ -21,38 +24,47 @@ pub(crate) fn expand_cfg(

ExpandResult::Ready(match parse_cfg(cx, sp, tts) {
Ok(cfg) => {
let matches_cfg = attr::cfg_matches(
let matches_cfg = attr::eval_config_entry(
cx.sess,
&cfg,
&cx.sess,
cx.current_expansion.lint_node_id,
Some(cx.ecfg.features),
);
ShouldEmit::ErrorsAndLints,
)
.as_bool();

MacEager::expr(cx.expr_bool(sp, matches_cfg))
}
Err(err) => {
let guar = err.emit();
DummyResult::any(sp, guar)
}
Err(guar) => DummyResult::any(sp, guar),
})
}

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

if p.token == token::Eof {
return Err(cx.dcx().create_err(errors::RequiresCfgPattern { span }));
fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result<CfgEntry, ErrorGuaranteed> {
let mut parser = cx.new_parser_from_tts(tts);
if parser.token == token::Eof {
return Err(cx.dcx().emit_err(errors::RequiresCfgPattern { span }));
}

let cfg = p.parse_meta_item_inner()?;
let meta = MetaItemOrLitParser::parse_single(&mut parser, ShouldEmit::ErrorsAndLints)
.map_err(|diag| diag.emit())?;
let cfg = AttributeParser::parse_single_args(
cx.sess,
span,
AttrStyle::Inner,
AttrPath { segments: vec![Ident::from_str("cfg")].into_boxed_slice(), span },
span,
CRATE_NODE_ID,
Some(cx.ecfg.features),
ShouldEmit::ErrorsAndLints,
&meta,
parse_cfg_entry,
&CFG_TEMPLATE,
)?;

let _ = p.eat(exp!(Comma));
let _ = parser.eat(exp!(Comma));

if !p.eat(exp!(Eof)) {
return Err(cx.dcx().create_err(errors::OneCfgPattern { span }));
if !parser.eat(exp!(Eof)) {
return Err(cx.dcx().emit_err(errors::OneCfgPattern { span }));
}

Ok(cfg)
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_error_codes/src/error_codes/E0536.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#### Note: this error code is no longer emitted by the compiler.

The `not` cfg-predicate was malformed.

Erroneous code example:

```compile_fail,E0536
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this old error code was correct

Copy link
Contributor Author

@JonathanBrouwer JonathanBrouwer Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old error code (E0536) is no longer emitted by anything, except cfg in the doc attribute. This example now emits the new error code.
I may change the example to use the doc attribute for now

```compile_fail,E0805
pub fn main() {
if cfg!(not()) { }
}
Expand Down
4 changes: 2 additions & 2 deletions 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 literal in `cfg` predicate value must be a boolean
cfg!(foo = 123); //~ ERROR literal in `cfg` predicate value must be a string
cfg!(123); //~ ERROR malformed `cfg` attribute input
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I like these diagnostics, it's not an attribute!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already a discussion open for this, that's why it's a draft :)

cfg!(foo = 123); //~ ERROR malformed `cfg` attribute input
cfg!(foo, bar); //~ ERROR expected 1 cfg-pattern
}
24 changes: 17 additions & 7 deletions tests/ui/macros/cfg.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,27 @@ error: macro requires a cfg-pattern as an argument
LL | cfg!();
| ^^^^^^ cfg-pattern required

error[E0565]: literal in `cfg` predicate value must be a boolean
--> $DIR/cfg.rs:3:10
error[E0539]: malformed `cfg` attribute input
--> $DIR/cfg.rs:3:5
|
LL | cfg!(123);
| ^^^
| ^^^^^---^
| | |
| | expected a valid identifier here
| help: must be of the form: `#![cfg(predicate)]`
Copy link
Contributor Author

@JonathanBrouwer JonathanBrouwer Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This help message is suggesting attribute related things, which is awful. Will fix in a separate PR that will go before this one (which is why this is in draft)

|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>

error[E0565]: literal in `cfg` predicate value must be a string
--> $DIR/cfg.rs:4:16
error[E0539]: malformed `cfg` attribute input
--> $DIR/cfg.rs:4:5
|
LL | cfg!(foo = 123);
| ^^^
| ^^^^^^^^^^^---^
| | |
| | expected a string literal here
| help: must be of the form: `#![cfg(predicate)]`
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>

error: expected 1 cfg-pattern
--> $DIR/cfg.rs:5:5
Expand All @@ -24,4 +34,4 @@ LL | cfg!(foo, bar);

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0565`.
For more information about this error, try `rustc --explain E0539`.
3 changes: 0 additions & 3 deletions tests/ui/span/E0536.rs

This file was deleted.

9 changes: 0 additions & 9 deletions tests/ui/span/E0536.stderr

This file was deleted.

3 changes: 3 additions & 0 deletions tests/ui/span/E0805.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub fn main() {
if cfg!(not()) { } //~ ERROR E0805
}
14 changes: 14 additions & 0 deletions tests/ui/span/E0805.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0805]: malformed `cfg` attribute input
--> $DIR/E0805.rs:2:8
|
LL | if cfg!(not()) { }
| ^^^^^^^^--^
| | |
| | expected a single argument here
| help: must be of the form: `#![cfg(predicate)]`
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0805`.
Loading