Auto merge of #147842 - matthiaskrgr:rollup-b4gp4p4, r=matthiaskrgr

Rollup of 6 pull requests

Successful merges:

 - rust-lang/rust#146509 (Result/Option layout guarantee clarifications)
 - rust-lang/rust#147494 (std::thread spawn: Docs: Link to Builder::spawn; Make same.)
 - rust-lang/rust#147532 ( Port `#[cfg_attr]` to the new attribute parsing infrastructure)
 - rust-lang/rust#147783 (bootstrap: migrate to object 0.37)
 - rust-lang/rust#147792 (Fix autodiff incorrectly applying fat-lto to proc-macro crates )
 - rust-lang/rust#147809 (rustdoc: Fix passes order so intra-doc links are collected after stripping passes)

Failed merges:

 - rust-lang/rust#147813 (Warn on unused_attributes in uitests )

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors
2025-10-18 15:48:08 +00:00
44 changed files with 721 additions and 265 deletions

View File

@@ -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

View File

@@ -1,19 +1,28 @@
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!(
@@ -21,7 +30,12 @@ pub const CFG_TEMPLATE: AttributeTemplate = template!(
"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> {
@@ -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)?
@@ -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;
}
},
@@ -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}"));
@@ -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))
}

View File

@@ -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;
@@ -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],
@@ -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],
@@ -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,
@@ -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")
};
@@ -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,
@@ -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)
}

View File

@@ -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};

View File

@@ -8,7 +8,7 @@ use std::fmt::{Debug, Display};
use rustc_ast::token::{self, Delimiter, MetaVarKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
use rustc_ast::{AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
use rustc_ast_pretty::pprust;
use rustc_errors::{Diag, PResult};
use rustc_hir::{self as hir, AttrPath};
@@ -124,7 +124,11 @@ impl<'a> ArgParser<'a> {
return None;
}
Self::List(MetaItemListParser::new(args, psess, should_emit)?)
Self::List(
MetaItemListParser::new(&args.tokens, args.dspan.entire(), psess, should_emit)
.map_err(|e| should_emit.emit_err(e))
.ok()?,
)
}
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
eq_span: *eq_span,
@@ -186,7 +190,15 @@ pub enum MetaItemOrLitParser<'a> {
Err(Span, ErrorGuaranteed),
}
impl<'a> MetaItemOrLitParser<'a> {
impl<'sess> MetaItemOrLitParser<'sess> {
pub fn parse_single(
parser: &mut Parser<'sess>,
should_emit: ShouldEmit,
) -> PResult<'sess, MetaItemOrLitParser<'static>> {
let mut this = MetaItemListParserContext { parser, should_emit };
this.parse_meta_item_inner()
}
pub fn span(&self) -> Span {
match self {
MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
@@ -204,7 +216,7 @@ impl<'a> MetaItemOrLitParser<'a> {
}
}
pub fn meta_item(&self) -> Option<&MetaItemParser<'a>> {
pub fn meta_item(&self) -> Option<&MetaItemParser<'sess>> {
match self {
MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
_ => None,
@@ -542,23 +554,13 @@ pub struct MetaItemListParser<'a> {
}
impl<'a> MetaItemListParser<'a> {
fn new<'sess>(
delim: &'a DelimArgs,
pub(crate) fn new<'sess>(
tokens: &'a TokenStream,
span: Span,
psess: &'sess ParseSess,
should_emit: ShouldEmit,
) -> Option<Self> {
match MetaItemListParserContext::parse(
delim.tokens.clone(),
psess,
delim.dspan.entire(),
should_emit,
) {
Ok(s) => Some(s),
Err(e) => {
should_emit.emit_err(e);
None
}
}
) -> Result<Self, Diag<'sess>> {
MetaItemListParserContext::parse(tokens.clone(), psess, span, should_emit)
}
/// Lets you pick and choose as what you want to parse each element in the list

View File

@@ -971,3 +971,12 @@ pub(crate) struct LimitInvalid<'a> {
pub value_span: Span,
pub error_str: &'a str,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_cfg_attr_bad_delim)]
pub(crate) struct CfgAttrBadDelim {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sugg: MetaBadDelimSugg,
}

View File

@@ -13,7 +13,7 @@ use rustc_ast::{
use rustc_attr_parsing as attr;
use rustc_attr_parsing::validate_attr::deny_builtin_meta_unsafety;
use rustc_attr_parsing::{
AttributeParser, CFG_TEMPLATE, EvalConfigResult, ShouldEmit, eval_config_entry, parse_cfg_attr,
AttributeParser, CFG_TEMPLATE, EvalConfigResult, ShouldEmit, eval_config_entry, parse_cfg,
validate_attr,
};
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
@@ -303,7 +303,7 @@ impl<'a> StripUnconfigured<'a> {
let trace_attr = attr_into_trace(cfg_attr.clone(), sym::cfg_attr_trace);
let Some((cfg_predicate, expanded_attrs)) =
rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess)
rustc_attr_parsing::parse_cfg_attr(cfg_attr, &self.sess, self.features)
else {
return vec![trace_attr];
};
@@ -318,7 +318,15 @@ impl<'a> StripUnconfigured<'a> {
);
}
if !attr::cfg_matches(&cfg_predicate, &self.sess, self.lint_node_id, self.features) {
if !attr::eval_config_entry(
self.sess,
&cfg_predicate,
ast::CRATE_NODE_ID,
self.features,
ShouldEmit::ErrorsAndLints,
)
.as_bool()
{
return vec![trace_attr];
}
@@ -428,7 +436,7 @@ impl<'a> StripUnconfigured<'a> {
node,
self.features,
emit_errors,
parse_cfg_attr,
parse_cfg,
&CFG_TEMPLATE,
) else {
// Cfg attribute was not parsable, give up
@@ -488,7 +496,7 @@ impl<'a> StripUnconfigured<'a> {
}
/// FIXME: Still used by Rustdoc, should be removed after
pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItemInner> {
pub fn parse_cfg_old<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItemInner> {
let span = meta_item.span;
match meta_item.meta_item_list() {
None => {

View File

@@ -814,7 +814,7 @@ fn get_nullable_type<'tcx>(
/// A type is niche-optimization candidate iff:
/// - Is a zero-sized type with alignment 1 (a “1-ZST”).
/// - Has no fields.
/// - Is either a struct/tuple with no fields, or an enum with no variants.
/// - Does not have the `#[non_exhaustive]` attribute.
fn is_niche_optimization_candidate<'tcx>(
tcx: TyCtxt<'tcx>,
@@ -828,7 +828,7 @@ fn is_niche_optimization_candidate<'tcx>(
match ty.kind() {
ty::Adt(ty_def, _) => {
let non_exhaustive = ty_def.is_variant_list_non_exhaustive();
let empty = (ty_def.is_struct() && ty_def.all_fields().next().is_none())
let empty = (ty_def.is_struct() && ty_def.non_enum_variant().fields.is_empty())
|| (ty_def.is_enum() && ty_def.variants().is_empty());
!non_exhaustive && empty

View File

@@ -122,7 +122,6 @@ parse_cannot_be_raw_lifetime = `{$ident}` cannot be a raw lifetime
parse_catch_after_try = keyword `catch` cannot follow a `try` block
.help = try using `match` on the result of the `try` block instead
parse_cfg_attr_bad_delim = wrong `cfg_attr` delimiters
parse_colon_as_semi = statements are terminated with a semicolon
.suggestion = use a semicolon instead
@@ -573,10 +572,6 @@ parse_macro_rules_missing_bang = expected `!` after `macro_rules`
parse_macro_rules_visibility = can't qualify macro_rules invocation with `{$vis}`
.suggestion = try exporting the macro
parse_malformed_cfg_attr = malformed `cfg_attr` attribute input
.suggestion = missing condition and attribute
.note = for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
parse_malformed_loop_label = malformed loop label
.suggestion = use the correct loop label format
@@ -610,8 +605,6 @@ parse_maybe_report_ambiguous_plus =
ambiguous `+` in a type
.suggestion = use parentheses to disambiguate
parse_meta_bad_delim_suggestion = the delimiters should be `(` and `)`
parse_mismatched_closing_delimiter = mismatched closing delimiter: `{$delimiter}`
.label_unmatched = mismatched closing delimiter
.label_opening_candidate = closing delimiter possibly meant for this

View File

@@ -3351,34 +3351,6 @@ pub(crate) struct KwBadCase<'a> {
pub kw: &'a str,
}
#[derive(Diagnostic)]
#[diag(parse_cfg_attr_bad_delim)]
pub(crate) struct CfgAttrBadDelim {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sugg: MetaBadDelimSugg,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parse_meta_bad_delim_suggestion, applicability = "machine-applicable")]
pub(crate) struct MetaBadDelimSugg {
#[suggestion_part(code = "(")]
pub open: Span,
#[suggestion_part(code = ")")]
pub close: Span,
}
#[derive(Diagnostic)]
#[diag(parse_malformed_cfg_attr)]
#[note]
pub(crate) struct MalformedCfgAttr {
#[primary_span]
#[suggestion(style = "verbose", code = "{sugg}")]
pub span: Span,
pub sugg: &'static str,
}
#[derive(Diagnostic)]
#[diag(parse_unknown_builtin_construct)]
pub(crate) struct UnknownBuiltinConstruct {

View File

@@ -18,8 +18,8 @@ use std::str::Utf8Error;
use std::sync::Arc;
use rustc_ast as ast;
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{AttrItem, Attribute, MetaItemInner, token};
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast_pretty::pprust;
use rustc_errors::{Diag, EmissionGuarantee, FatalError, PResult, pluralize};
use rustc_session::parse::ParseSess;
@@ -32,7 +32,6 @@ pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments");
#[macro_use]
pub mod parser;
use parser::Parser;
use rustc_ast::token::Delimiter;
use crate::lexer::StripTokens;
@@ -230,45 +229,3 @@ pub fn fake_token_stream_for_crate(psess: &ParseSess, krate: &ast::Crate) -> Tok
Some(krate.spans.inner_span),
))
}
pub fn parse_cfg_attr(
cfg_attr: &Attribute,
psess: &ParseSess,
) -> Option<(MetaItemInner, 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>";
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(psess, dspan, delim);
match parse_in(psess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
Ok(r) => return Some(r),
Err(e) => {
e.with_help(format!("the valid syntax is `{CFG_ATTR_GRAMMAR_HELP}`"))
.with_note(CFG_ATTR_NOTE_REF)
.emit();
}
}
}
_ => {
psess.dcx().emit_err(errors::MalformedCfgAttr {
span: cfg_attr.span,
sugg: CFG_ATTR_GRAMMAR_HELP,
});
}
}
None
}
fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
if let Delimiter::Parenthesis = delim {
return;
}
psess.dcx().emit_err(errors::CfgAttrBadDelim {
span: span.entire(),
sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close },
});
}

View File

@@ -377,27 +377,6 @@ impl<'a> Parser<'a> {
Ok(lit)
}
/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
pub fn parse_cfg_attr(
&mut self,
) -> PResult<'a, (ast::MetaItemInner, Vec<(ast::AttrItem, Span)>)> {
let cfg_predicate = self.parse_meta_item_inner()?;
self.expect(exp!(Comma))?;
// Presumably, the majority of the time there will only be one attr.
let mut expanded_attrs = Vec::with_capacity(1);
while self.token != token::Eof {
let lo = self.token.span;
let item = self.parse_attr_item(ForceCollect::Yes)?;
expanded_attrs.push((item, lo.to(self.prev_token.span)));
if !self.eat(exp!(Comma)) {
break;
}
}
Ok((cfg_predicate, expanded_attrs))
}
/// Matches `COMMASEP(meta_item_inner)`.
pub fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec<ast::MetaItemInner>> {
// Presumably, the majority of the time there will only be one attr.

View File

@@ -603,7 +603,8 @@ impl Session {
// Autodiff currently requires fat-lto to have access to the llvm-ir of all (indirectly) used functions and types.
// fat-lto is the easiest solution to this requirement, but quite expensive.
// FIXME(autodiff): Make autodiff also work with embed-bc instead of fat-lto.
if self.opts.autodiff_enabled() {
// Don't apply fat-lto to proc-macro crates as they cannot use fat-lto without -Zdylib-lto
if self.opts.autodiff_enabled() && !self.opts.crate_types.contains(&CrateType::ProcMacro) {
return config::Lto::Fat;
}