11use rustc_ast:: token:: Delimiter ;
22use rustc_ast:: tokenstream:: DelimSpan ;
3- use rustc_ast:: { AttrItem , Attribute , LitKind , MetaItemInner , NodeId , ast, token} ;
4- use rustc_errors:: PResult ;
3+ use rustc_ast:: { AttrItem , Attribute , CRATE_NODE_ID , LitKind , NodeId , ast, token} ;
4+ use rustc_errors:: { Applicability , PResult } ;
55use rustc_feature:: { AttributeTemplate , Features , template} ;
6- use rustc_hir:: RustcVersion ;
76use rustc_hir:: attrs:: CfgEntry ;
7+ use rustc_hir:: { AttrPath , RustcVersion } ;
88use rustc_parse:: parser:: { ForceCollect , Parser } ;
99use rustc_parse:: { exp, parse_in} ;
1010use rustc_session:: Session ;
@@ -17,16 +17,24 @@ use thin_vec::ThinVec;
1717
1818use crate :: context:: { AcceptContext , ShouldEmit , Stage } ;
1919use crate :: parser:: { ArgParser , MetaItemListParser , MetaItemOrLitParser , NameValueParser } ;
20- use crate :: session_diagnostics:: { CfgAttrBadDelim , MalformedCfgAttr , MetaBadDelimSugg } ;
20+ use crate :: session_diagnostics:: {
21+ AttributeParseError , AttributeParseErrorReason , CfgAttrBadDelim , MetaBadDelimSugg ,
22+ } ;
2123use crate :: {
22- CfgMatchesLintEmitter , fluent_generated, parse_version, session_diagnostics, try_gate_cfg,
24+ AttributeParser , CfgMatchesLintEmitter , fluent_generated, parse_version, session_diagnostics,
25+ try_gate_cfg,
2326} ;
2427
2528pub const CFG_TEMPLATE : AttributeTemplate = template ! (
2629 List : & [ "predicate" ] ,
2730 "https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute"
2831) ;
2932
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+
3038pub fn parse_cfg < ' c , S : Stage > (
3139 cx : & ' c mut AcceptContext < ' _ , ' _ , S > ,
3240 args : & ' c ArgParser < ' _ > ,
@@ -76,9 +84,7 @@ pub(crate) fn parse_cfg_entry<S: Stage>(
7684 } ,
7785 a @ ( ArgParser :: NoArgs | ArgParser :: NameValue ( _) ) => {
7886 let Some ( name) = meta. path ( ) . word_sym ( ) else {
79- cx. emit_err ( session_diagnostics:: CfgPredicateIdentifier {
80- span : meta. path ( ) . span ( ) ,
81- } ) ;
87+ cx. expected_identifier ( meta. path ( ) . span ( ) ) ;
8288 return None ;
8389 } ;
8490 parse_name_value ( name, meta. path ( ) . span ( ) , a. name_value ( ) , meta. span ( ) , cx) ?
@@ -87,7 +93,7 @@ pub(crate) fn parse_cfg_entry<S: Stage>(
8793 MetaItemOrLitParser :: Lit ( lit) => match lit. kind {
8894 LitKind :: Bool ( b) => CfgEntry :: Bool ( b, lit. span ) ,
8995 _ => {
90- cx. emit_err ( session_diagnostics :: CfgPredicateIdentifier { span : lit. span } ) ;
96+ cx. expected_identifier ( lit. span ) ;
9197 return None ;
9298 }
9399 } ,
@@ -155,9 +161,7 @@ fn parse_cfg_entry_target<S: Stage>(
155161
156162 // Then, parse it as a name-value item
157163 let Some ( name) = sub_item. path ( ) . word_sym ( ) else {
158- cx. emit_err ( session_diagnostics:: CfgPredicateIdentifier {
159- span : sub_item. path ( ) . span ( ) ,
160- } ) ;
164+ cx. expected_identifier ( sub_item. path ( ) . span ( ) ) ;
161165 return None ;
162166 } ;
163167 let name = Symbol :: intern ( & format ! ( "target_{name}" ) ) ;
@@ -309,32 +313,51 @@ impl EvalConfigResult {
309313
310314pub fn parse_cfg_attr (
311315 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-
316+ sess : & Session ,
317+ features : Option < & Features > ,
318+ ) -> Option < ( CfgEntry , Vec < ( AttrItem , Span ) > ) > {
318319 match cfg_attr. get_normal_item ( ) . args {
319320 ast:: AttrArgs :: Delimited ( ast:: DelimArgs { dspan, delim, ref tokens } )
320321 if !tokens. is_empty ( ) =>
321322 {
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)
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 )
325326 } ) {
326327 Ok ( r) => return Some ( r) ,
327328 Err ( e) => {
328- e. with_help ( format ! ( "the valid syntax is `{CFG_ATTR_GRAMMAR_HELP}`" ) )
329- . with_note ( CFG_ATTR_NOTE_REF )
330- . emit ( ) ;
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 ( ) ;
331341 }
332342 }
333343 }
334344 _ => {
335- psess
336- . dcx ( )
337- . emit_err ( MalformedCfgAttr { span : cfg_attr. span , sugg : CFG_ATTR_GRAMMAR_HELP } ) ;
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+ } ) ;
338361 }
339362 }
340363 None
@@ -353,8 +376,42 @@ fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter
353376/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
354377fn parse_cfg_attr_internal < ' a > (
355378 parser : & mut Parser < ' a > ,
356- ) -> PResult < ' a , ( ast:: MetaItemInner , Vec < ( ast:: AttrItem , Span ) > ) > {
357- let cfg_predicate = parser. parse_meta_item_inner ( ) ?;
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+
358415 parser. expect ( exp ! ( Comma ) ) ?;
359416
360417 // Presumably, the majority of the time there will only be one attr.
0 commit comments