diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 8b6b762f43102..0fc05f3f7f047 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -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 @@ -150,6 +152,9 @@ attr_parsing_link_requires_name = `#[link]` attribute requires a `name = "string"` argument .label = missing `name` argument +attr_parsing_malformed_cfg_attr = malformed `cfg_attr` attribute input + .suggestion = missing condition and attribute + .note = for more information, visit attr_parsing_meta_bad_delim = wrong meta list delimiters attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)` diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 7085561107978..faa50580809aa 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -1,19 +1,26 @@ -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::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::{CfgAttrBadDelim, MalformedCfgAttr, 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 +28,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 { @@ -300,3 +312,101 @@ impl EvalConfigResult { } } } + +pub fn parse_cfg_attr( + cfg_attr: &Attribute, + sess: &Session, + features: Option<&Features>, +) -> Option<(CfgEntry, 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 \ + "; + + 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) => { + e.with_help(format!("the valid syntax is `{CFG_ATTR_GRAMMAR_HELP}`")) + .with_note(CFG_ATTR_NOTE_REF) + .emit(); + } + } + } + _ => { + sess.dcx() + .emit_err(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(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_sub( + 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)) +} diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 8f2de4af14e00..0dd2c308e6f0f 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -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; @@ -121,13 +121,6 @@ impl<'sess> AttributeParser<'sess, Early> { parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> Option, template: &AttributeTemplate, ) -> Option { - 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 +129,41 @@ 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_sub( + sess, + attr.span, + attr.style, + path.get_attribute_path(), + target_span, + target_node_id, + features, + emit_errors, + args, + parse_fn, + template, + ) + } + + pub fn parse_single_sub( + 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, + template: &AttributeTemplate, + ) -> Option { + 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 +173,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) } diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index f51cc8c4e8be7..bcd0d674c75f1 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -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}; diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 3f4f567901575..7474471f2fe0f 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -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 { - 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> { + MetaItemListParserContext::parse(tokens.clone(), psess, span, should_emit) } /// Lets you pick and choose as what you want to parse each element in the list diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 1194ac5872cb2..db82776310b35 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -971,3 +971,22 @@ 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, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_malformed_cfg_attr)] +#[note] +pub(crate) struct MalformedCfgAttr { + #[primary_span] + #[suggestion(style = "verbose", code = "{sugg}")] + pub span: Span, + pub sugg: &'static str, +} diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 2925e337071ca..8278c29570b75 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -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 => { diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index b1894ab92192e..52a35c98a98b5 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -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 - 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 diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 1abeee6fe433e..6d536aa850b3d 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -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 { diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index c26c7b9122afa..edec44ca95019 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -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 \ - "; - - 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 }, - }); -} diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index acd338156ce88..5725f4c366796 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -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> { // Presumably, the majority of the time there will only be one attr. diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 881a81b22f0f7..6c77e41965dc5 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -751,7 +751,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator } for attr in doc_cfg { if let Some(cfg_mi) = - attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg(attr, sess)) + attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg_old(attr, sess)) { match Cfg::parse(cfg_mi) { Ok(new_cfg) => cfg_info.current_cfg &= new_cfg,