Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0e2130c
std::thread spawn: Docs: Link to Builder::spawn; Make same.
evanj Oct 8, 2025
52cc311
Rename some functions
JonathanBrouwer Oct 3, 2025
e0c190f
Move `parse_cfg_attr` to rustc_attr_parsing
JonathanBrouwer Oct 3, 2025
7113d58
Port `#[cfg_attr]` to the new attribute parsing infrastructure
JonathanBrouwer Oct 3, 2025
090dad0
Changes in uitests for `cfg_attr`
JonathanBrouwer Oct 14, 2025
c4dc39b
add link to Builder (code review improvement)
evanj Oct 16, 2025
10a5334
bootstrap: migrate to object 0.37
durin42 Jun 17, 2025
fcc47d0
rustdoc: Fix passes order so intra-doc links are collected after stri…
GuillaumeGomez Oct 17, 2025
e67d502
Fix autodiff incorrectly applying fat-lto to proc-macro crates
osamakader Oct 16, 2025
b3bb750
Add regression tests for intra-doc links
GuillaumeGomez Oct 17, 2025
29a19f8
Result/Option layout guarantee clarifications
RalfJung Sep 13, 2025
781432e
clarify 'no fields'
RalfJung Sep 13, 2025
058f08d
have Result docs match ABI docs
RalfJung Sep 13, 2025
73f5fe7
Revise `Result`/`Option` guarantee docs
traviscross Oct 18, 2025
32d21cb
Rollup merge of #146509 - RalfJung:res-opt-layout-guarantees, r=travi…
matthiaskrgr Oct 18, 2025
55a3df8
Rollup merge of #147494 - evanj:evan.jones/thread-spawn-link, r=joboet
matthiaskrgr Oct 18, 2025
2b582ea
Rollup merge of #147532 - JonathanBrouwer:cfg_attr2, r=jdonszelmann
matthiaskrgr Oct 18, 2025
063b26a
Rollup merge of #147783 - durin42:object-unification, r=Zalathar
matthiaskrgr Oct 18, 2025
7fa2030
Rollup merge of #147792 - osamakader:fix-autodiff-proc-macro-lto, r=p…
matthiaskrgr Oct 18, 2025
27b0018
Rollup merge of #147809 - GuillaumeGomez:fix-rustdoc-passes, r=fmease
matthiaskrgr Oct 18, 2025
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
2 changes: 2 additions & 0 deletions compiler/rustc_attr_parsing/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ attr_parsing_as_needed_compatibility =
attr_parsing_bundle_needs_static =
linking modifier `bundle` is only compatible with `static` linking kind

attr_parsing_cfg_attr_bad_delim = wrong `cfg_attr` delimiters

attr_parsing_cfg_predicate_identifier =
`cfg` predicate key must be an identifier

Expand Down
151 changes: 139 additions & 12 deletions compiler/rustc_attr_parsing/src/attributes/cfg.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,41 @@
use rustc_ast::{LitKind, NodeId};
use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, NodeId, ast, token};
use rustc_errors::{Applicability, PResult};
use rustc_feature::{AttributeTemplate, Features, template};
use rustc_hir::RustcVersion;
use rustc_hir::attrs::CfgEntry;
use rustc_hir::{AttrPath, RustcVersion};
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::{exp, parse_in};
use rustc_session::Session;
use rustc_session::config::ExpectedValues;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::parse::feature_err;
use rustc_session::parse::{ParseSess, feature_err};
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;

use crate::context::{AcceptContext, ShouldEmit, Stage};
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser};
use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg,
};
use crate::{
CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg,
AttributeParser, CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics,
try_gate_cfg,
};

pub const CFG_TEMPLATE: AttributeTemplate = template!(
List: &["predicate"],
"https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute"
);

pub fn parse_cfg_attr<'c, S: Stage>(
const CFG_ATTR_TEMPLATE: AttributeTemplate = template!(
List: &["predicate, attr1, attr2, ..."],
"https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute"
);

pub fn parse_cfg<'c, S: Stage>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> Option<CfgEntry> {
Expand Down Expand Up @@ -70,9 +84,7 @@ pub(crate) fn parse_cfg_entry<S: Stage>(
},
a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
let Some(name) = meta.path().word_sym() else {
cx.emit_err(session_diagnostics::CfgPredicateIdentifier {
span: meta.path().span(),
});
cx.expected_identifier(meta.path().span());
return None;
};
parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)?
Expand All @@ -81,7 +93,7 @@ pub(crate) fn parse_cfg_entry<S: Stage>(
MetaItemOrLitParser::Lit(lit) => match lit.kind {
LitKind::Bool(b) => CfgEntry::Bool(b, lit.span),
_ => {
cx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: lit.span });
cx.expected_identifier(lit.span);
return None;
}
},
Expand Down Expand Up @@ -149,9 +161,7 @@ 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.emit_err(session_diagnostics::CfgPredicateIdentifier {
span: sub_item.path().span(),
});
cx.expected_identifier(sub_item.path().span());
return None;
};
let name = Symbol::intern(&format!("target_{name}"));
Expand Down Expand Up @@ -300,3 +310,120 @@ impl EvalConfigResult {
}
}
}

pub fn parse_cfg_attr(
cfg_attr: &Attribute,
sess: &Session,
features: Option<&Features>,
) -> Option<(CfgEntry, Vec<(AttrItem, Span)>)> {
match cfg_attr.get_normal_item().args {
ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens })
if !tokens.is_empty() =>
{
check_cfg_attr_bad_delim(&sess.psess, dspan, delim);
match parse_in(&sess.psess, tokens.clone(), "`cfg_attr` input", |p| {
parse_cfg_attr_internal(p, sess, features, cfg_attr)
}) {
Ok(r) => return Some(r),
Err(e) => {
let suggestions = CFG_ATTR_TEMPLATE.suggestions(cfg_attr.style, sym::cfg_attr);
e.with_span_suggestions(
cfg_attr.span,
"must be of the form",
suggestions,
Applicability::HasPlaceholders,
)
.with_note(format!(
"for more information, visit <{}>",
CFG_ATTR_TEMPLATE.docs.expect("cfg_attr has docs")
))
.emit();
}
}
}
_ => {
let (span, reason) = if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) =
cfg_attr.get_normal_item().args
{
(dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument)
} else {
(cfg_attr.span, AttributeParseErrorReason::ExpectedList)
};

sess.dcx().emit_err(AttributeParseError {
span,
attr_span: cfg_attr.span,
template: CFG_ATTR_TEMPLATE,
attribute: AttrPath::from_ast(&cfg_attr.get_normal_item().path),
reason,
attr_style: cfg_attr.style,
});
}
}
None
}

fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
if let Delimiter::Parenthesis = delim {
return;
}
psess.dcx().emit_err(CfgAttrBadDelim {
span: span.entire(),
sugg: MetaBadDelimSugg { open: span.open, close: span.close },
});
}

/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
fn parse_cfg_attr_internal<'a>(
parser: &mut Parser<'a>,
sess: &'a Session,
features: Option<&Features>,
attribute: &Attribute,
) -> PResult<'a, (CfgEntry, Vec<(ast::AttrItem, Span)>)> {
// Parse cfg predicate
let pred_start = parser.token.span;
let meta = MetaItemOrLitParser::parse_single(parser, ShouldEmit::ErrorsAndLints)?;
let pred_span = pred_start.with_hi(parser.token.span.hi());

let cfg_predicate = AttributeParser::parse_single_args(
sess,
attribute.span,
attribute.style,
AttrPath {
segments: attribute
.ident_path()
.expect("cfg_attr is not a doc comment")
.into_boxed_slice(),
span: attribute.span,
},
pred_span,
CRATE_NODE_ID,
features,
ShouldEmit::ErrorsAndLints,
&meta,
parse_cfg_entry,
&CFG_ATTR_TEMPLATE,
)
.ok_or_else(|| {
let mut diag = sess.dcx().struct_err(
"cfg_entry parsing failing with `ShouldEmit::ErrorsAndLints` should emit a error.",
);
diag.downgrade_to_delayed_bug();
diag
})?;

parser.expect(exp!(Comma))?;

// Presumably, the majority of the time there will only be one attr.
let mut expanded_attrs = Vec::with_capacity(1);
while parser.token != token::Eof {
let lo = parser.token.span;
let item = parser.parse_attr_item(ForceCollect::Yes)?;
expanded_attrs.push((item, lo.to(parser.prev_token.span)));
if !parser.eat(exp!(Comma)) {
break;
}
}

Ok((cfg_predicate, expanded_attrs))
}
64 changes: 52 additions & 12 deletions compiler/rustc_attr_parsing/src/interface.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::borrow::Cow;

use rustc_ast as ast;
use rustc_ast::NodeId;
use rustc_ast::{AttrStyle, NodeId};
use rustc_errors::DiagCtxtHandle;
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::AttributeKind;
Expand Down Expand Up @@ -62,7 +62,8 @@ impl<'sess> AttributeParser<'sess, Early> {
)
}

/// Usually you want `parse_limited`, which defaults to no errors.
/// This does the same as `parse_limited`, except it has a `should_emit` parameter which allows it to emit errors.
/// Usually you want `parse_limited`, which emits no errors.
pub fn parse_limited_should_emit(
sess: &'sess Session,
attrs: &[ast::Attribute],
Expand All @@ -86,6 +87,13 @@ impl<'sess> AttributeParser<'sess, Early> {
parsed.pop()
}

/// This method allows you to parse a list of attributes *before* `rustc_ast_lowering`.
/// This can be used for attributes that would be removed before `rustc_ast_lowering`, such as attributes on macro calls.
///
/// Try to use this as little as possible. Attributes *should* be lowered during
/// `rustc_ast_lowering`. Some attributes require access to features to parse, which would
/// crash if you tried to do so through [`parse_limited_all`](Self::parse_limited_all).
/// Therefore, if `parse_only` is None, then features *must* be provided.
pub fn parse_limited_all(
sess: &'sess Session,
attrs: &[ast::Attribute],
Expand All @@ -111,6 +119,8 @@ impl<'sess> AttributeParser<'sess, Early> {
)
}

/// This method parses a single attribute, using `parse_fn`.
/// This is useful if you already know what exact attribute this is, and want to parse it.
pub fn parse_single<T>(
sess: &'sess Session,
attr: &ast::Attribute,
Expand All @@ -121,13 +131,6 @@ impl<'sess> AttributeParser<'sess, Early> {
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> Option<T>,
template: &AttributeTemplate,
) -> Option<T> {
let mut parser = Self {
features,
tools: Vec::new(),
parse_only: None,
sess,
stage: Early { emit_errors },
};
let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
panic!("parse_single called on a doc attr")
};
Expand All @@ -136,6 +139,43 @@ impl<'sess> AttributeParser<'sess, Early> {
let meta_parser = MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors)?;
let path = meta_parser.path();
let args = meta_parser.args();
Self::parse_single_args(
sess,
attr.span,
attr.style,
path.get_attribute_path(),
target_span,
target_node_id,
features,
emit_errors,
args,
parse_fn,
template,
)
}

/// This method is equivalent to `parse_single`, but parses arguments using `parse_fn` using manually created `args`.
/// This is useful when you want to parse other things than attributes using attribute parsers.
pub fn parse_single_args<T, I>(
sess: &'sess Session,
attr_span: Span,
attr_style: AttrStyle,
attr_path: AttrPath,
target_span: Span,
target_node_id: NodeId,
features: Option<&'sess Features>,
emit_errors: ShouldEmit,
args: &I,
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &I) -> Option<T>,
template: &AttributeTemplate,
) -> Option<T> {
let mut parser = Self {
features,
tools: Vec::new(),
parse_only: None,
sess,
stage: Early { emit_errors },
};
let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
shared: SharedContext {
cx: &mut parser,
Expand All @@ -145,10 +185,10 @@ impl<'sess> AttributeParser<'sess, Early> {
crate::lints::emit_attribute_lint(&lint, sess);
},
},
attr_span: attr.span,
attr_style: attr.style,
attr_span,
attr_style,
template,
attr_path: path.get_attribute_path(),
attr_path,
};
parse_fn(&mut cx, args)
}
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_attr_parsing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ mod session_diagnostics;
mod target_checking;
pub mod validate_attr;

pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr};
pub use attributes::cfg::{
CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg, parse_cfg_attr,
};
pub use attributes::cfg_old::*;
pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
pub use context::{Early, Late, OmitDoc, ShouldEmit};
Expand Down
Loading
Loading