@@ -21,6 +21,7 @@ use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey,
21
21
use session:: Session ;
22
22
use syntax:: ast;
23
23
use syntax:: attr;
24
+ use syntax:: feature_gate;
24
25
use syntax:: source_map:: MultiSpan ;
25
26
use syntax:: symbol:: Symbol ;
26
27
use util:: nodemap:: FxHashMap ;
@@ -199,8 +200,7 @@ impl<'a> LintLevelsBuilder<'a> {
199
200
let store = self . sess . lint_store . borrow ( ) ;
200
201
let sess = self . sess ;
201
202
let bad_attr = |span| {
202
- span_err ! ( sess, span, E0452 ,
203
- "malformed lint attribute" ) ;
203
+ struct_span_err ! ( sess, span, E0452 , "malformed lint attribute" )
204
204
} ;
205
205
for attr in attrs {
206
206
let level = match Level :: from_str ( & attr. name ( ) . as_str ( ) ) {
@@ -211,19 +211,76 @@ impl<'a> LintLevelsBuilder<'a> {
211
211
let meta = unwrap_or ! ( attr. meta( ) , continue ) ;
212
212
attr:: mark_used ( attr) ;
213
213
214
- let metas = if let Some ( metas) = meta. meta_item_list ( ) {
214
+ let mut metas = if let Some ( metas) = meta. meta_item_list ( ) {
215
215
metas
216
216
} else {
217
- bad_attr ( meta. span ) ;
218
- continue
217
+ let mut err = bad_attr ( meta. span ) ;
218
+ err. emit ( ) ;
219
+ continue ;
219
220
} ;
220
221
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
+
221
270
for li in metas {
222
271
let word = match li. word ( ) {
223
272
Some ( word) => word,
224
273
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 ;
227
284
}
228
285
} ;
229
286
let tool_name = if let Some ( lint_tool) = word. is_scoped ( ) {
@@ -245,7 +302,7 @@ impl<'a> LintLevelsBuilder<'a> {
245
302
let name = word. name ( ) ;
246
303
match store. check_lint_name ( & name. as_str ( ) , tool_name) {
247
304
CheckLintNameResult :: Ok ( ids) => {
248
- let src = LintSource :: Node ( name, li. span ) ;
305
+ let src = LintSource :: Node ( name, li. span , reason ) ;
249
306
for id in ids {
250
307
specs. insert ( * id, ( level, src) ) ;
251
308
}
@@ -255,7 +312,9 @@ impl<'a> LintLevelsBuilder<'a> {
255
312
match result {
256
313
Ok ( ids) => {
257
314
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
+ ) ;
259
318
for id in ids {
260
319
specs. insert ( * id, ( level, src) ) ;
261
320
}
@@ -286,7 +345,9 @@ impl<'a> LintLevelsBuilder<'a> {
286
345
Applicability :: MachineApplicable ,
287
346
) . emit ( ) ;
288
347
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
+ ) ;
290
351
for id in ids {
291
352
specs. insert ( * id, ( level, src) ) ;
292
353
}
@@ -368,11 +429,11 @@ impl<'a> LintLevelsBuilder<'a> {
368
429
} ;
369
430
let forbidden_lint_name = match forbid_src {
370
431
LintSource :: Default => id. to_string ( ) ,
371
- LintSource :: Node ( name, _) => name. to_string ( ) ,
432
+ LintSource :: Node ( name, _, _ ) => name. to_string ( ) ,
372
433
LintSource :: CommandLine ( name) => name. to_string ( ) ,
373
434
} ;
374
435
let ( lint_attr_name, lint_attr_span) = match * src {
375
- LintSource :: Node ( name, span) => ( name, span) ,
436
+ LintSource :: Node ( name, span, _ ) => ( name, span) ,
376
437
_ => continue ,
377
438
} ;
378
439
let mut diag_builder = struct_span_err ! ( self . sess,
@@ -384,15 +445,19 @@ impl<'a> LintLevelsBuilder<'a> {
384
445
forbidden_lint_name) ;
385
446
diag_builder. span_label ( lint_attr_span, "overruled by previous forbid" ) ;
386
447
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 ) => {
389
450
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
+ }
391
455
} ,
392
456
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" ) ;
394
458
}
395
- } . emit ( ) ;
459
+ }
460
+ diag_builder. emit ( ) ;
396
461
// don't set a separate error for every lint in the group
397
462
break
398
463
}
0 commit comments