88//!
99//! [#64197]: https://github.com/rust-lang/rust/issues/64197
1010
11- use crate :: validate_attr;
11+ use crate :: { parse_in , validate_attr} ;
1212use rustc_feature:: Features ;
1313use rustc_errors:: Applicability ;
1414use syntax:: attr:: HasAttrs ;
1515use syntax:: feature_gate:: { feature_err, get_features} ;
1616use syntax:: attr;
17- use syntax:: ast;
17+ use syntax:: ast:: { self , Attribute , AttrItem , MetaItem } ;
1818use syntax:: edition:: Edition ;
1919use syntax:: mut_visit:: * ;
2020use syntax:: ptr:: P ;
21+ use syntax:: tokenstream:: DelimSpan ;
2122use syntax:: sess:: ParseSess ;
2223use syntax:: util:: map_in_place:: MapInPlace ;
24+ use syntax_pos:: Span ;
2325use syntax_pos:: symbol:: sym;
2426
2527use smallvec:: SmallVec ;
@@ -72,6 +74,11 @@ macro_rules! configure {
7274 }
7375}
7476
77+ const CFG_ATTR_GRAMMAR_HELP : & str = "#[cfg_attr(condition, attribute, other_attribute, ...)]" ;
78+ const CFG_ATTR_NOTE_REF : & str = "for more information, visit \
79+ <https://doc.rust-lang.org/reference/conditional-compilation.html\
80+ #the-cfg_attr-attribute>";
81+
7582impl < ' a > StripUnconfigured < ' a > {
7683 pub fn configure < T : HasAttrs > ( & mut self , mut node : T ) -> Option < T > {
7784 self . process_cfg_attrs ( & mut node) ;
@@ -97,34 +104,14 @@ impl<'a> StripUnconfigured<'a> {
97104 /// Gives a compiler warning when the `cfg_attr` contains no attributes and
98105 /// is in the original source file. Gives a compiler error if the syntax of
99106 /// the attribute is incorrect.
100- fn process_cfg_attr ( & mut self , attr : ast :: Attribute ) -> Vec < ast :: Attribute > {
107+ fn process_cfg_attr ( & mut self , attr : Attribute ) -> Vec < Attribute > {
101108 if !attr. has_name ( sym:: cfg_attr) {
102109 return vec ! [ attr] ;
103110 }
104- if let ast:: MacArgs :: Empty = attr. get_normal_item ( ) . args {
105- self . sess . span_diagnostic
106- . struct_span_err (
107- attr. span ,
108- "malformed `cfg_attr` attribute input" ,
109- ) . span_suggestion (
110- attr. span ,
111- "missing condition and attribute" ,
112- "#[cfg_attr(condition, attribute, other_attribute, ...)]" . to_owned ( ) ,
113- Applicability :: HasPlaceholders ,
114- ) . note ( "for more information, visit \
115- <https://doc.rust-lang.org/reference/conditional-compilation.html\
116- #the-cfg_attr-attribute>")
117- . emit ( ) ;
118- return vec ! [ ] ;
119- }
120111
121- let res = crate :: parse_in_attr ( self . sess , & attr, |p| p. parse_cfg_attr ( ) ) ;
122- let ( cfg_predicate, expanded_attrs) = match res {
123- Ok ( result) => result,
124- Err ( mut e) => {
125- e. emit ( ) ;
126- return vec ! [ ] ;
127- }
112+ let ( cfg_predicate, expanded_attrs) = match self . parse_cfg_attr ( & attr) {
113+ None => return vec ! [ ] ,
114+ Some ( r) => r,
128115 } ;
129116
130117 // Lint on zero attributes in source.
@@ -135,24 +122,72 @@ impl<'a> StripUnconfigured<'a> {
135122 // At this point we know the attribute is considered used.
136123 attr:: mark_used ( & attr) ;
137124
138- if attr:: cfg_matches ( & cfg_predicate, self . sess , self . features ) {
139- // We call `process_cfg_attr` recursively in case there's a
140- // `cfg_attr` inside of another `cfg_attr`. E.g.
141- // `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
142- expanded_attrs. into_iter ( )
143- . flat_map ( |( item, span) | self . process_cfg_attr ( attr:: mk_attr_from_item (
144- attr. style ,
145- item,
146- span,
147- ) ) )
125+ if !attr:: cfg_matches ( & cfg_predicate, self . sess , self . features ) {
126+ return vec ! [ ] ;
127+ }
128+
129+ // We call `process_cfg_attr` recursively in case there's a
130+ // `cfg_attr` inside of another `cfg_attr`. E.g.
131+ // `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
132+ expanded_attrs
133+ . into_iter ( )
134+ . flat_map ( |( item, span) | {
135+ let attr = attr:: mk_attr_from_item ( attr. style , item, span) ;
136+ self . process_cfg_attr ( attr)
137+ } )
148138 . collect ( )
149- } else {
150- vec ! [ ]
139+ }
140+
141+ fn parse_cfg_attr ( & self , attr : & Attribute ) -> Option < ( MetaItem , Vec < ( AttrItem , Span ) > ) > {
142+ match & attr. get_normal_item ( ) . args {
143+ ast:: MacArgs :: Delimited ( dspan, delim, tts) if !tts. is_empty ( ) => {
144+ if let ast:: MacDelimiter :: Brace | ast:: MacDelimiter :: Bracket = delim {
145+ self . error_malformed_cfg_attr_wrong_delim ( * dspan) ;
146+ }
147+ match parse_in ( self . sess , tts. clone ( ) , "`cfg_attr` input" , |p| p. parse_cfg_attr ( ) ) {
148+ Ok ( r) => return Some ( r) ,
149+ Err ( mut e) => e
150+ . help ( & format ! ( "the valid syntax is `{}`" , CFG_ATTR_GRAMMAR_HELP ) )
151+ . note ( CFG_ATTR_NOTE_REF )
152+ . emit ( ) ,
153+ }
154+ }
155+ _ => self . error_malformed_cfg_attr_missing ( attr. span ) ,
151156 }
157+ None
158+ }
159+
160+ fn error_malformed_cfg_attr_wrong_delim ( & self , dspan : DelimSpan ) {
161+ self . sess
162+ . span_diagnostic
163+ . struct_span_err ( dspan. entire ( ) , "wrong `cfg_attr` delimiters" )
164+ . multipart_suggestion (
165+ "the delimiters should be `(` and `)`" ,
166+ vec ! [
167+ ( dspan. open, "(" . to_string( ) ) ,
168+ ( dspan. close, ")" . to_string( ) ) ,
169+ ] ,
170+ Applicability :: MachineApplicable ,
171+ )
172+ . emit ( ) ;
173+ }
174+
175+ fn error_malformed_cfg_attr_missing ( & self , span : Span ) {
176+ self . sess
177+ . span_diagnostic
178+ . struct_span_err ( span, "malformed `cfg_attr` attribute input" )
179+ . span_suggestion (
180+ span,
181+ "missing condition and attribute" ,
182+ CFG_ATTR_GRAMMAR_HELP . to_string ( ) ,
183+ Applicability :: HasPlaceholders ,
184+ )
185+ . note ( CFG_ATTR_NOTE_REF )
186+ . emit ( ) ;
152187 }
153188
154189 /// Determines if a node with the given attributes should be included in this configuration.
155- pub fn in_cfg ( & self , attrs : & [ ast :: Attribute ] ) -> bool {
190+ pub fn in_cfg ( & self , attrs : & [ Attribute ] ) -> bool {
156191 attrs. iter ( ) . all ( |attr| {
157192 if !is_cfg ( attr) {
158193 return true ;
@@ -199,15 +234,15 @@ impl<'a> StripUnconfigured<'a> {
199234 }
200235
201236 /// Visit attributes on expression and statements (but not attributes on items in blocks).
202- fn visit_expr_attrs ( & mut self , attrs : & [ ast :: Attribute ] ) {
237+ fn visit_expr_attrs ( & mut self , attrs : & [ Attribute ] ) {
203238 // flag the offending attributes
204239 for attr in attrs. iter ( ) {
205240 self . maybe_emit_expr_attr_err ( attr) ;
206241 }
207242 }
208243
209244 /// If attributes are not allowed on expressions, emit an error for `attr`
210- pub fn maybe_emit_expr_attr_err ( & self , attr : & ast :: Attribute ) {
245+ pub fn maybe_emit_expr_attr_err ( & self , attr : & Attribute ) {
211246 if !self . features . map ( |features| features. stmt_expr_attributes ) . unwrap_or ( true ) {
212247 let mut err = feature_err ( self . sess ,
213248 sym:: stmt_expr_attributes,
@@ -350,7 +385,7 @@ impl<'a> MutVisitor for StripUnconfigured<'a> {
350385 }
351386}
352387
353- fn is_cfg ( attr : & ast :: Attribute ) -> bool {
388+ fn is_cfg ( attr : & Attribute ) -> bool {
354389 attr. check_name ( sym:: cfg)
355390}
356391
@@ -359,8 +394,8 @@ fn is_cfg(attr: &ast::Attribute) -> bool {
359394pub fn process_configure_mod (
360395 sess : & ParseSess ,
361396 cfg_mods : bool ,
362- attrs : & [ ast :: Attribute ] ,
363- ) -> ( bool , Vec < ast :: Attribute > ) {
397+ attrs : & [ Attribute ] ,
398+ ) -> ( bool , Vec < Attribute > ) {
364399 // Don't perform gated feature checking.
365400 let mut strip_unconfigured = StripUnconfigured { sess, features : None } ;
366401 let mut attrs = attrs. to_owned ( ) ;
0 commit comments