@@ -19,7 +19,9 @@ use syntax::ext::base::{Annotatable, MacroKind, SyntaxExtension};
1919use syntax:: ext:: expand:: { AstFragment , Invocation , InvocationKind } ;
2020use syntax:: ext:: hygiene:: { self , Mark } ;
2121use 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+ } ;
2325use syntax:: symbol:: { Symbol , keywords} ;
2426use syntax:: visit:: Visitor ;
2527use syntax:: util:: lev_distance:: find_best_match_for_name;
@@ -310,15 +312,18 @@ impl<'a> Resolver<'a> {
310312 if !features. rustc_attrs {
311313 let msg = "unless otherwise specified, attributes with the prefix \
312314 `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" ) ;
315316 }
316317 } else if !features. custom_attribute {
317318 let msg = format ! ( "The attribute `{}` is currently unknown to the \
318319 compiler and may have meaning added to it in the \
319320 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+ ) ;
322327 }
323328 }
324329 } else {
@@ -339,6 +344,61 @@ impl<'a> Resolver<'a> {
339344 Ok ( ( def, self . get_macro ( def) ) )
340345 }
341346
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+
342402 pub fn resolve_macro_to_def_inner (
343403 & mut self ,
344404 path : & ast:: Path ,
0 commit comments