@@ -19,7 +19,9 @@ use syntax::ext::base::{Annotatable, MacroKind, SyntaxExtension};
19
19
use syntax:: ext:: expand:: { AstFragment , Invocation , InvocationKind } ;
20
20
use syntax:: ext:: hygiene:: { self , Mark } ;
21
21
use syntax:: ext:: tt:: macro_rules;
22
- use syntax:: feature_gate:: { feature_err, is_builtin_attr_name, GateIssue } ;
22
+ use syntax:: feature_gate:: {
23
+ feature_err, is_builtin_attr_name, AttributeGate , GateIssue , Stability , BUILTIN_ATTRIBUTES ,
24
+ } ;
23
25
use syntax:: symbol:: { Symbol , keywords} ;
24
26
use syntax:: visit:: Visitor ;
25
27
use syntax:: util:: lev_distance:: find_best_match_for_name;
@@ -310,15 +312,18 @@ impl<'a> Resolver<'a> {
310
312
if !features. rustc_attrs {
311
313
let msg = "unless otherwise specified, attributes with the prefix \
312
314
`rustc_` are reserved for internal compiler diagnostics";
313
- feature_err ( & self . session . parse_sess , "rustc_attrs" , path. span ,
314
- GateIssue :: Language , & msg) . emit ( ) ;
315
+ self . report_unknown_attribute ( path. span , & name, msg, "rustc_attrs" ) ;
315
316
}
316
317
} else if !features. custom_attribute {
317
318
let msg = format ! ( "The attribute `{}` is currently unknown to the \
318
319
compiler and may have meaning added to it in the \
319
320
future", path) ;
320
- feature_err ( & self . session . parse_sess , "custom_attribute" , path. span ,
321
- GateIssue :: Language , & msg) . emit ( ) ;
321
+ self . report_unknown_attribute (
322
+ path. span ,
323
+ & name,
324
+ & msg,
325
+ "custom_attribute" ,
326
+ ) ;
322
327
}
323
328
}
324
329
} else {
@@ -339,6 +344,61 @@ impl<'a> Resolver<'a> {
339
344
Ok ( ( def, self . get_macro ( def) ) )
340
345
}
341
346
347
+ fn report_unknown_attribute ( & self , span : Span , name : & str , msg : & str , feature : & str ) {
348
+ let mut err = feature_err (
349
+ & self . session . parse_sess ,
350
+ feature,
351
+ span,
352
+ GateIssue :: Language ,
353
+ & msg,
354
+ ) ;
355
+
356
+ let features = self . session . features_untracked ( ) ;
357
+
358
+ let attr_candidates = BUILTIN_ATTRIBUTES
359
+ . iter ( )
360
+ . filter_map ( |( name, _, _, gate) | {
361
+ if name. starts_with ( "rustc_" ) && !features. rustc_attrs {
362
+ return None ;
363
+ }
364
+
365
+ match gate {
366
+ AttributeGate :: Gated ( Stability :: Unstable , ..)
367
+ if self . session . opts . unstable_features . is_nightly_build ( ) =>
368
+ {
369
+ Some ( name)
370
+ }
371
+ AttributeGate :: Gated ( Stability :: Deprecated ( ..) , ..) => Some ( name) ,
372
+ AttributeGate :: Ungated => Some ( name) ,
373
+ _ => None ,
374
+ }
375
+ } )
376
+ . map ( |name| Symbol :: intern ( name) )
377
+ . chain (
378
+ // Add built-in macro attributes as well.
379
+ self . builtin_macros . iter ( ) . filter_map ( |( name, binding) | {
380
+ match binding. macro_kind ( ) {
381
+ Some ( MacroKind :: Attr ) => Some ( * name) ,
382
+ _ => None ,
383
+ }
384
+ } ) ,
385
+ )
386
+ . collect :: < Vec < _ > > ( ) ;
387
+
388
+ let lev_suggestion = find_best_match_for_name ( attr_candidates. iter ( ) , & name, None ) ;
389
+
390
+ if let Some ( suggestion) = lev_suggestion {
391
+ err. span_suggestion (
392
+ span,
393
+ "a built-in attribute with a similar name exists" ,
394
+ suggestion. to_string ( ) ,
395
+ Applicability :: MaybeIncorrect ,
396
+ ) ;
397
+ }
398
+
399
+ err. emit ( ) ;
400
+ }
401
+
342
402
pub fn resolve_macro_to_def_inner (
343
403
& mut self ,
344
404
path : & ast:: Path ,
0 commit comments