|
1 | | -use rustc_ast::{LitKind, NodeId}; |
| 1 | +use rustc_ast::token::Delimiter; |
| 2 | +use rustc_ast::tokenstream::DelimSpan; |
| 3 | +use rustc_ast::{AttrItem, Attribute, LitKind, MetaItemInner, NodeId, ast, token}; |
| 4 | +use rustc_errors::PResult; |
2 | 5 | use rustc_feature::{AttributeTemplate, Features, template}; |
3 | 6 | use rustc_hir::RustcVersion; |
4 | 7 | use rustc_hir::attrs::CfgEntry; |
| 8 | +use rustc_parse::parser::{ForceCollect, Parser}; |
| 9 | +use rustc_parse::{exp, parse_in}; |
5 | 10 | use rustc_session::Session; |
6 | 11 | use rustc_session::config::ExpectedValues; |
7 | 12 | use rustc_session::lint::BuiltinLintDiag; |
8 | 13 | use rustc_session::lint::builtin::UNEXPECTED_CFGS; |
9 | | -use rustc_session::parse::feature_err; |
| 14 | +use rustc_session::parse::{ParseSess, feature_err}; |
10 | 15 | use rustc_span::{Span, Symbol, sym}; |
11 | 16 | use thin_vec::ThinVec; |
12 | 17 |
|
13 | 18 | use crate::context::{AcceptContext, ShouldEmit, Stage}; |
14 | 19 | use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser}; |
| 20 | +use crate::session_diagnostics::{CfgAttrBadDelim, MalformedCfgAttr, MetaBadDelimSugg}; |
15 | 21 | use crate::{ |
16 | 22 | CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg, |
17 | 23 | }; |
@@ -301,3 +307,66 @@ impl EvalConfigResult { |
301 | 307 | } |
302 | 308 | } |
303 | 309 |
|
| 310 | +pub fn parse_cfg_attr( |
| 311 | + cfg_attr: &Attribute, |
| 312 | + psess: &ParseSess, |
| 313 | +) -> Option<(MetaItemInner, Vec<(AttrItem, Span)>)> { |
| 314 | + const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]"; |
| 315 | + const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ |
| 316 | + <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>"; |
| 317 | + |
| 318 | + match cfg_attr.get_normal_item().args { |
| 319 | + ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens }) |
| 320 | + if !tokens.is_empty() => |
| 321 | + { |
| 322 | + check_cfg_attr_bad_delim(psess, dspan, delim); |
| 323 | + match parse_in(psess, tokens.clone(), "`cfg_attr` input", |p| { |
| 324 | + parse_cfg_attr_internal(p) |
| 325 | + }) { |
| 326 | + Ok(r) => return Some(r), |
| 327 | + Err(e) => { |
| 328 | + e.with_help(format!("the valid syntax is `{CFG_ATTR_GRAMMAR_HELP}`")) |
| 329 | + .with_note(CFG_ATTR_NOTE_REF) |
| 330 | + .emit(); |
| 331 | + } |
| 332 | + } |
| 333 | + } |
| 334 | + _ => { |
| 335 | + psess |
| 336 | + .dcx() |
| 337 | + .emit_err(MalformedCfgAttr { span: cfg_attr.span, sugg: CFG_ATTR_GRAMMAR_HELP }); |
| 338 | + } |
| 339 | + } |
| 340 | + None |
| 341 | +} |
| 342 | + |
| 343 | +fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { |
| 344 | + if let Delimiter::Parenthesis = delim { |
| 345 | + return; |
| 346 | + } |
| 347 | + psess.dcx().emit_err(CfgAttrBadDelim { |
| 348 | + span: span.entire(), |
| 349 | + sugg: MetaBadDelimSugg { open: span.open, close: span.close }, |
| 350 | + }); |
| 351 | +} |
| 352 | + |
| 353 | +/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited. |
| 354 | +fn parse_cfg_attr_internal<'a>( |
| 355 | + parser: &mut Parser<'a>, |
| 356 | +) -> PResult<'a, (ast::MetaItemInner, Vec<(ast::AttrItem, Span)>)> { |
| 357 | + let cfg_predicate = parser.parse_meta_item_inner()?; |
| 358 | + parser.expect(exp!(Comma))?; |
| 359 | + |
| 360 | + // Presumably, the majority of the time there will only be one attr. |
| 361 | + let mut expanded_attrs = Vec::with_capacity(1); |
| 362 | + while parser.token != token::Eof { |
| 363 | + let lo = parser.token.span; |
| 364 | + let item = parser.parse_attr_item(ForceCollect::Yes)?; |
| 365 | + expanded_attrs.push((item, lo.to(parser.prev_token.span))); |
| 366 | + if !parser.eat(exp!(Comma)) { |
| 367 | + break; |
| 368 | + } |
| 369 | + } |
| 370 | + |
| 371 | + Ok((cfg_predicate, expanded_attrs)) |
| 372 | +} |
0 commit comments