@@ -21,6 +21,7 @@ use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey,
2121use session:: Session ;
2222use syntax:: ast;
2323use syntax:: attr;
24+ use syntax:: feature_gate;
2425use syntax:: source_map:: MultiSpan ;
2526use syntax:: symbol:: Symbol ;
2627use util:: nodemap:: FxHashMap ;
@@ -199,8 +200,7 @@ impl<'a> LintLevelsBuilder<'a> {
199200 let store = self . sess . lint_store . borrow ( ) ;
200201 let sess = self . sess ;
201202 let bad_attr = |span| {
202- span_err ! ( sess, span, E0452 ,
203- "malformed lint attribute" ) ;
203+ struct_span_err ! ( sess, span, E0452 , "malformed lint attribute" )
204204 } ;
205205 for attr in attrs {
206206 let level = match Level :: from_str ( & attr. name ( ) . as_str ( ) ) {
@@ -211,19 +211,76 @@ impl<'a> LintLevelsBuilder<'a> {
211211 let meta = unwrap_or ! ( attr. meta( ) , continue ) ;
212212 attr:: mark_used ( attr) ;
213213
214- let metas = if let Some ( metas) = meta. meta_item_list ( ) {
214+ let mut metas = if let Some ( metas) = meta. meta_item_list ( ) {
215215 metas
216216 } else {
217- bad_attr ( meta. span ) ;
218- continue
217+ let mut err = bad_attr ( meta. span ) ;
218+ err. emit ( ) ;
219+ continue ;
219220 } ;
220221
222+ if metas. is_empty ( ) {
223+ // FIXME (#55112): issue unused-attributes lint for `#[level()]`
224+ continue ;
225+ }
226+
227+ // Before processing the lint names, look for a reason (RFC 2383)
228+ // at the end.
229+ let mut reason = None ;
230+ let tail_li = & metas[ metas. len ( ) -1 ] ;
231+ if let Some ( item) = tail_li. meta_item ( ) {
232+ match item. node {
233+ ast:: MetaItemKind :: Word => { } // actual lint names handled later
234+ ast:: MetaItemKind :: NameValue ( ref name_value) => {
235+ let gate_reasons = !self . sess . features_untracked ( ) . lint_reasons ;
236+ if item. ident == "reason" {
237+ // found reason, reslice meta list to exclude it
238+ metas = & metas[ 0 ..metas. len ( ) -1 ] ;
239+ // FIXME (#55112): issue unused-attributes lint if we thereby
240+ // don't have any lint names (`#[level(reason = "foo")]`)
241+ if let ast:: LitKind :: Str ( rationale, _) = name_value. node {
242+ if gate_reasons {
243+ feature_gate:: emit_feature_err (
244+ & self . sess . parse_sess ,
245+ "lint_reasons" ,
246+ item. span ,
247+ feature_gate:: GateIssue :: Language ,
248+ "lint reasons are experimental"
249+ ) ;
250+ } else {
251+ reason = Some ( rationale) ;
252+ }
253+ } else {
254+ let mut err = bad_attr ( name_value. span ) ;
255+ err. help ( "reason must be a string literal" ) ;
256+ err. emit ( ) ;
257+ }
258+ } else {
259+ let mut err = bad_attr ( item. span ) ;
260+ err. emit ( ) ;
261+ }
262+ } ,
263+ ast:: MetaItemKind :: List ( _) => {
264+ let mut err = bad_attr ( item. span ) ;
265+ err. emit ( ) ;
266+ }
267+ }
268+ }
269+
221270 for li in metas {
222271 let word = match li. word ( ) {
223272 Some ( word) => word,
224273 None => {
225- bad_attr ( li. span ) ;
226- continue
274+ let mut err = bad_attr ( li. span ) ;
275+ if let Some ( item) = li. meta_item ( ) {
276+ if let ast:: MetaItemKind :: NameValue ( _) = item. node {
277+ if item. ident == "reason" {
278+ err. help ( "reason in lint attribute must come last" ) ;
279+ }
280+ }
281+ }
282+ err. emit ( ) ;
283+ continue ;
227284 }
228285 } ;
229286 let tool_name = if let Some ( lint_tool) = word. is_scoped ( ) {
@@ -245,7 +302,7 @@ impl<'a> LintLevelsBuilder<'a> {
245302 let name = word. name ( ) ;
246303 match store. check_lint_name ( & name. as_str ( ) , tool_name) {
247304 CheckLintNameResult :: Ok ( ids) => {
248- let src = LintSource :: Node ( name, li. span ) ;
305+ let src = LintSource :: Node ( name, li. span , reason ) ;
249306 for id in ids {
250307 specs. insert ( * id, ( level, src) ) ;
251308 }
@@ -255,7 +312,9 @@ impl<'a> LintLevelsBuilder<'a> {
255312 match result {
256313 Ok ( ids) => {
257314 let complete_name = & format ! ( "{}::{}" , tool_name. unwrap( ) , name) ;
258- let src = LintSource :: Node ( Symbol :: intern ( complete_name) , li. span ) ;
315+ let src = LintSource :: Node (
316+ Symbol :: intern ( complete_name) , li. span , reason
317+ ) ;
259318 for id in ids {
260319 specs. insert ( * id, ( level, src) ) ;
261320 }
@@ -286,7 +345,9 @@ impl<'a> LintLevelsBuilder<'a> {
286345 Applicability :: MachineApplicable ,
287346 ) . emit ( ) ;
288347
289- let src = LintSource :: Node ( Symbol :: intern ( & new_lint_name) , li. span ) ;
348+ let src = LintSource :: Node (
349+ Symbol :: intern ( & new_lint_name) , li. span , reason
350+ ) ;
290351 for id in ids {
291352 specs. insert ( * id, ( level, src) ) ;
292353 }
@@ -368,11 +429,11 @@ impl<'a> LintLevelsBuilder<'a> {
368429 } ;
369430 let forbidden_lint_name = match forbid_src {
370431 LintSource :: Default => id. to_string ( ) ,
371- LintSource :: Node ( name, _) => name. to_string ( ) ,
432+ LintSource :: Node ( name, _, _ ) => name. to_string ( ) ,
372433 LintSource :: CommandLine ( name) => name. to_string ( ) ,
373434 } ;
374435 let ( lint_attr_name, lint_attr_span) = match * src {
375- LintSource :: Node ( name, span) => ( name, span) ,
436+ LintSource :: Node ( name, span, _ ) => ( name, span) ,
376437 _ => continue ,
377438 } ;
378439 let mut diag_builder = struct_span_err ! ( self . sess,
@@ -384,15 +445,19 @@ impl<'a> LintLevelsBuilder<'a> {
384445 forbidden_lint_name) ;
385446 diag_builder. span_label ( lint_attr_span, "overruled by previous forbid" ) ;
386447 match forbid_src {
387- LintSource :: Default => & mut diag_builder ,
388- LintSource :: Node ( _, forbid_source_span) => {
448+ LintSource :: Default => { } ,
449+ LintSource :: Node ( _, forbid_source_span, reason ) => {
389450 diag_builder. span_label ( forbid_source_span,
390- "`forbid` level set here" )
451+ "`forbid` level set here" ) ;
452+ if let Some ( rationale) = reason {
453+ diag_builder. note ( & rationale. as_str ( ) ) ;
454+ }
391455 } ,
392456 LintSource :: CommandLine ( _) => {
393- diag_builder. note ( "`forbid` lint level was set on command line" )
457+ diag_builder. note ( "`forbid` lint level was set on command line" ) ;
394458 }
395- } . emit ( ) ;
459+ }
460+ diag_builder. emit ( ) ;
396461 // don't set a separate error for every lint in the group
397462 break
398463 }
0 commit comments