1- use rustc_ast:: { LitKind , NodeId } ;
1+ use rustc_ast:: token:: Delimiter ;
2+ use rustc_ast:: tokenstream:: DelimSpan ;
3+ use rustc_ast:: { AttrItem , Attribute , CRATE_NODE_ID , LitKind , NodeId , ast, token} ;
4+ use rustc_errors:: { Applicability , PResult } ;
25use rustc_feature:: { AttributeTemplate , Features , template} ;
3- use rustc_hir:: RustcVersion ;
46use rustc_hir:: attrs:: CfgEntry ;
7+ use rustc_hir:: { AttrPath , RustcVersion } ;
8+ use rustc_parse:: parser:: { ForceCollect , Parser } ;
9+ use rustc_parse:: { exp, parse_in} ;
510use rustc_session:: Session ;
611use rustc_session:: config:: ExpectedValues ;
712use rustc_session:: lint:: BuiltinLintDiag ;
813use rustc_session:: lint:: builtin:: UNEXPECTED_CFGS ;
9- use rustc_session:: parse:: feature_err;
14+ use rustc_session:: parse:: { ParseSess , feature_err} ;
1015use rustc_span:: { Span , Symbol , sym} ;
1116use thin_vec:: ThinVec ;
1217
1318use crate :: context:: { AcceptContext , ShouldEmit , Stage } ;
1419use crate :: parser:: { ArgParser , MetaItemListParser , MetaItemOrLitParser , NameValueParser } ;
20+ use crate :: session_diagnostics:: {
21+ AttributeParseError , AttributeParseErrorReason , CfgAttrBadDelim , MetaBadDelimSugg ,
22+ } ;
1523use crate :: {
16- CfgMatchesLintEmitter , fluent_generated, parse_version, session_diagnostics, try_gate_cfg,
24+ AttributeParser , CfgMatchesLintEmitter , fluent_generated, parse_version, session_diagnostics,
25+ try_gate_cfg,
1726} ;
1827
1928pub const CFG_TEMPLATE : AttributeTemplate = template ! (
2029 List : & [ "predicate" ] ,
2130 "https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute"
2231) ;
2332
24- pub fn parse_cfg_attr < ' c , S : Stage > (
33+ const CFG_ATTR_TEMPLATE : AttributeTemplate = template ! (
34+ List : & [ "predicate, attr1, attr2, ..." ] ,
35+ "https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute"
36+ ) ;
37+
38+ pub fn parse_cfg < ' c , S : Stage > (
2539 cx : & ' c mut AcceptContext < ' _ , ' _ , S > ,
2640 args : & ' c ArgParser < ' _ > ,
2741) -> Option < CfgEntry > {
@@ -70,9 +84,7 @@ pub(crate) fn parse_cfg_entry<S: Stage>(
7084 } ,
7185 a @ ( ArgParser :: NoArgs | ArgParser :: NameValue ( _) ) => {
7286 let Some ( name) = meta. path ( ) . word_sym ( ) else {
73- cx. emit_err ( session_diagnostics:: CfgPredicateIdentifier {
74- span : meta. path ( ) . span ( ) ,
75- } ) ;
87+ cx. expected_identifier ( meta. path ( ) . span ( ) ) ;
7688 return None ;
7789 } ;
7890 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>(
8193 MetaItemOrLitParser :: Lit ( lit) => match lit. kind {
8294 LitKind :: Bool ( b) => CfgEntry :: Bool ( b, lit. span ) ,
8395 _ => {
84- cx. emit_err ( session_diagnostics :: CfgPredicateIdentifier { span : lit. span } ) ;
96+ cx. expected_identifier ( lit. span ) ;
8597 return None ;
8698 }
8799 } ,
@@ -149,9 +161,7 @@ fn parse_cfg_entry_target<S: Stage>(
149161
150162 // Then, parse it as a name-value item
151163 let Some ( name) = sub_item. path ( ) . word_sym ( ) else {
152- cx. emit_err ( session_diagnostics:: CfgPredicateIdentifier {
153- span : sub_item. path ( ) . span ( ) ,
154- } ) ;
164+ cx. expected_identifier ( sub_item. path ( ) . span ( ) ) ;
155165 return None ;
156166 } ;
157167 let name = Symbol :: intern ( & format ! ( "target_{name}" ) ) ;
@@ -300,3 +310,120 @@ impl EvalConfigResult {
300310 }
301311 }
302312}
313+
314+ pub fn parse_cfg_attr (
315+ cfg_attr : & Attribute ,
316+ sess : & Session ,
317+ features : Option < & Features > ,
318+ ) -> Option < ( CfgEntry , Vec < ( AttrItem , Span ) > ) > {
319+ match cfg_attr. get_normal_item ( ) . args {
320+ ast:: AttrArgs :: Delimited ( ast:: DelimArgs { dspan, delim, ref tokens } )
321+ if !tokens. is_empty ( ) =>
322+ {
323+ check_cfg_attr_bad_delim ( & sess. psess , dspan, delim) ;
324+ match parse_in ( & sess. psess , tokens. clone ( ) , "`cfg_attr` input" , |p| {
325+ parse_cfg_attr_internal ( p, sess, features, cfg_attr)
326+ } ) {
327+ Ok ( r) => return Some ( r) ,
328+ Err ( e) => {
329+ let suggestions = CFG_ATTR_TEMPLATE . suggestions ( cfg_attr. style , sym:: cfg_attr) ;
330+ e. with_span_suggestions (
331+ cfg_attr. span ,
332+ "must be of the form" ,
333+ suggestions,
334+ Applicability :: HasPlaceholders ,
335+ )
336+ . with_note ( format ! (
337+ "for more information, visit <{}>" ,
338+ CFG_ATTR_TEMPLATE . docs. expect( "cfg_attr has docs" )
339+ ) )
340+ . emit ( ) ;
341+ }
342+ }
343+ }
344+ _ => {
345+ let ( span, reason) = if let ast:: AttrArgs :: Delimited ( ast:: DelimArgs { dspan, .. } ) =
346+ cfg_attr. get_normal_item ( ) . args
347+ {
348+ ( dspan. entire ( ) , AttributeParseErrorReason :: ExpectedAtLeastOneArgument )
349+ } else {
350+ ( cfg_attr. span , AttributeParseErrorReason :: ExpectedList )
351+ } ;
352+
353+ sess. dcx ( ) . emit_err ( AttributeParseError {
354+ span,
355+ attr_span : cfg_attr. span ,
356+ template : CFG_ATTR_TEMPLATE ,
357+ attribute : AttrPath :: from_ast ( & cfg_attr. get_normal_item ( ) . path ) ,
358+ reason,
359+ attr_style : cfg_attr. style ,
360+ } ) ;
361+ }
362+ }
363+ None
364+ }
365+
366+ fn check_cfg_attr_bad_delim ( psess : & ParseSess , span : DelimSpan , delim : Delimiter ) {
367+ if let Delimiter :: Parenthesis = delim {
368+ return ;
369+ }
370+ psess. dcx ( ) . emit_err ( CfgAttrBadDelim {
371+ span : span. entire ( ) ,
372+ sugg : MetaBadDelimSugg { open : span. open , close : span. close } ,
373+ } ) ;
374+ }
375+
376+ /// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
377+ fn parse_cfg_attr_internal < ' a > (
378+ parser : & mut Parser < ' a > ,
379+ sess : & ' a Session ,
380+ features : Option < & Features > ,
381+ attribute : & Attribute ,
382+ ) -> PResult < ' a , ( CfgEntry , Vec < ( ast:: AttrItem , Span ) > ) > {
383+ // Parse cfg predicate
384+ let pred_start = parser. token . span ;
385+ let meta = MetaItemOrLitParser :: parse_single ( parser, ShouldEmit :: ErrorsAndLints ) ?;
386+ let pred_span = pred_start. with_hi ( parser. token . span . hi ( ) ) ;
387+
388+ let cfg_predicate = AttributeParser :: parse_single_args (
389+ sess,
390+ attribute. span ,
391+ attribute. style ,
392+ AttrPath {
393+ segments : attribute
394+ . ident_path ( )
395+ . expect ( "cfg_attr is not a doc comment" )
396+ . into_boxed_slice ( ) ,
397+ span : attribute. span ,
398+ } ,
399+ pred_span,
400+ CRATE_NODE_ID ,
401+ features,
402+ ShouldEmit :: ErrorsAndLints ,
403+ & meta,
404+ parse_cfg_entry,
405+ & CFG_ATTR_TEMPLATE ,
406+ )
407+ . ok_or_else ( || {
408+ let mut diag = sess. dcx ( ) . struct_err (
409+ "cfg_entry parsing failing with `ShouldEmit::ErrorsAndLints` should emit a error." ,
410+ ) ;
411+ diag. downgrade_to_delayed_bug ( ) ;
412+ diag
413+ } ) ?;
414+
415+ parser. expect ( exp ! ( Comma ) ) ?;
416+
417+ // Presumably, the majority of the time there will only be one attr.
418+ let mut expanded_attrs = Vec :: with_capacity ( 1 ) ;
419+ while parser. token != token:: Eof {
420+ let lo = parser. token . span ;
421+ let item = parser. parse_attr_item ( ForceCollect :: Yes ) ?;
422+ expanded_attrs. push ( ( item, lo. to ( parser. prev_token . span ) ) ) ;
423+ if !parser. eat ( exp ! ( Comma ) ) {
424+ break ;
425+ }
426+ }
427+
428+ Ok ( ( cfg_predicate, expanded_attrs) )
429+ }
0 commit comments