@@ -4,7 +4,7 @@ use rustc_ast::ptr::P;
44use rustc_ast:: visit:: { self , Visitor } ;
55use rustc_ast:: { self as ast, Crate , ItemKind , ModKind , NodeId , Path , CRATE_NODE_ID } ;
66use rustc_ast_pretty:: pprust;
7- use rustc_data_structures:: fx:: FxHashSet ;
7+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
88use rustc_errors:: {
99 pluralize, Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed , MultiSpan ,
1010} ;
@@ -1016,6 +1016,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
10161016 parent_scope,
10171017 false ,
10181018 false ,
1019+ None ,
10191020 ) {
10201021 suggestions. extend (
10211022 ext. helper_attrs
@@ -1331,15 +1332,126 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
13311332 macro_kind : MacroKind ,
13321333 parent_scope : & ParentScope < ' a > ,
13331334 ident : Ident ,
1335+ sugg_span : Option < Span > ,
13341336 ) {
1337+ // Bring imported but unused `derive` macros into `macro_map` so we ensure they can be used
1338+ // for suggestions.
1339+ self . visit_scopes (
1340+ ScopeSet :: Macro ( MacroKind :: Derive ) ,
1341+ & parent_scope,
1342+ ident. span . ctxt ( ) ,
1343+ |this, scope, _use_prelude, _ctxt| {
1344+ let Scope :: Module ( m, _) = scope else { return None ; } ;
1345+ for ( _, resolution) in this. resolutions ( m) . borrow ( ) . iter ( ) {
1346+ let Some ( binding) = resolution. borrow ( ) . binding else { continue ; } ;
1347+ let Res :: Def (
1348+ DefKind :: Macro ( MacroKind :: Derive | MacroKind :: Attr ) ,
1349+ def_id,
1350+ ) = binding. res ( ) else { continue ; } ;
1351+ // By doing this all *imported* macros get added to the `macro_map` even if they
1352+ // are *unused*, which makes the later suggestions find them and work.
1353+ let _ = this. get_macro_by_def_id ( def_id) ;
1354+ }
1355+ None :: < ( ) >
1356+ } ,
1357+ ) ;
1358+
13351359 let is_expected = & |res : Res | res. macro_kind ( ) == Some ( macro_kind) ;
13361360 let suggestion = self . early_lookup_typo_candidate (
13371361 ScopeSet :: Macro ( macro_kind) ,
13381362 parent_scope,
13391363 ident,
13401364 is_expected,
13411365 ) ;
1342- self . add_typo_suggestion ( err, suggestion, ident. span ) ;
1366+ if !self . add_typo_suggestion ( err, suggestion, ident. span ) {
1367+ // FIXME: this only works if the macro that has the helper_attr has already
1368+ // been imported.
1369+ let mut derives = vec ! [ ] ;
1370+ let mut all_attrs: FxHashMap < Symbol , Vec < _ > > = FxHashMap :: default ( ) ;
1371+ for ( def_id, data) in & self . macro_map {
1372+ for helper_attr in & data. ext . helper_attrs {
1373+ let item_name = self . tcx . item_name ( * def_id) ;
1374+ all_attrs. entry ( * helper_attr) . or_default ( ) . push ( item_name) ;
1375+ if helper_attr == & ident. name {
1376+ // FIXME: we should also do Levenshtein distance checks here.
1377+ derives. push ( item_name) ;
1378+ }
1379+ }
1380+ }
1381+ let kind = MacroKind :: Derive . descr ( ) ;
1382+ if !derives. is_empty ( ) {
1383+ derives. sort ( ) ;
1384+ derives. dedup ( ) ;
1385+ let msg = match & derives[ ..] {
1386+ [ derive] => format ! ( " `{derive}`" ) ,
1387+ [ start @ .., last] => format ! (
1388+ "s {} and `{last}`" ,
1389+ start. iter( ) . map( |d| format!( "`{d}`" ) ) . collect:: <Vec <_>>( ) . join( ", " )
1390+ ) ,
1391+ [ ] => unreachable ! ( "we checked for this to be non-empty 10 lines above!?" ) ,
1392+ } ;
1393+ let msg = format ! (
1394+ "`{}` is an attribute that can be used by the {kind}{msg}, you might be missing a \
1395+ `derive` attribute",
1396+ ident. name,
1397+ ) ;
1398+ let sugg_span =
1399+ if let ModuleKind :: Def ( DefKind :: Enum , id, _) = parent_scope. module . kind {
1400+ let span = self . def_span ( id) ;
1401+ if span. from_expansion ( ) {
1402+ None
1403+ } else {
1404+ // For enum variants, `sugg_span` is empty, but we can get the `enum`'s `Span`.
1405+ Some ( span. shrink_to_lo ( ) )
1406+ }
1407+ } else {
1408+ // For items, this `Span` will be populated, everything else it'll be `None`.
1409+ sugg_span
1410+ } ;
1411+ match sugg_span {
1412+ Some ( span) => {
1413+ err. span_suggestion_verbose (
1414+ span,
1415+ & msg,
1416+ format ! (
1417+ "#[derive({})]\n " ,
1418+ derives
1419+ . iter( )
1420+ . map( |d| d. to_string( ) )
1421+ . collect:: <Vec <String >>( )
1422+ . join( ", " )
1423+ ) ,
1424+ Applicability :: MaybeIncorrect ,
1425+ ) ;
1426+ }
1427+ None => {
1428+ err. note ( & msg) ;
1429+ }
1430+ }
1431+ } else {
1432+ let all_attr_names: Vec < Symbol > = all_attrs. keys ( ) . cloned ( ) . collect ( ) ;
1433+ if let Some ( best_match) = find_best_match_for_name ( & all_attr_names, ident. name , None )
1434+ && let Some ( macros) = all_attrs. get ( & best_match)
1435+ && !macros. is_empty ( )
1436+ {
1437+ let msg = match & macros[ ..] {
1438+ [ ] => unreachable ! ( "we checked above in the if-let" ) ,
1439+ [ name] => format ! ( " `{name}` accepts" ) ,
1440+ [ start @ .., end] => format ! (
1441+ "s {} and `{end}` accept" ,
1442+ start. iter( ) . map( |m| format!( "`{m}`" ) ) . collect:: <Vec <_>>( ) . join( ", " ) ,
1443+ ) ,
1444+ } ;
1445+ let msg = format ! ( "the {kind}{msg} the similarly named `{best_match}` attribute" ) ;
1446+ err. span_suggestion_verbose (
1447+ ident. span ,
1448+ & msg,
1449+ best_match,
1450+ Applicability :: MaybeIncorrect ,
1451+ ) ;
1452+ }
1453+ }
1454+ }
13431455
13441456 let import_suggestions =
13451457 self . lookup_import_candidates ( ident, Namespace :: MacroNS , parent_scope, is_expected) ;
@@ -1364,14 +1476,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
13641476 err. help ( "have you added the `#[macro_use]` on the module/import?" ) ;
13651477 return ;
13661478 }
1479+
13671480 if ident. name == kw:: Default
13681481 && let ModuleKind :: Def ( DefKind :: Enum , def_id, _) = parent_scope. module . kind
13691482 {
13701483 let span = self . def_span ( def_id) ;
13711484 let source_map = self . tcx . sess . source_map ( ) ;
13721485 let head_span = source_map. guess_head_span ( span) ;
1373- if let Ok ( head) = source_map. span_to_snippet ( head_span) {
1374- err. span_suggestion ( head_span, "consider adding a derive" , format ! ( "#[derive(Default)]\n {head}" ) , Applicability :: MaybeIncorrect ) ;
1486+ if let Ok ( _) = source_map. span_to_snippet ( head_span) {
1487+ err. span_suggestion (
1488+ head_span. shrink_to_lo ( ) ,
1489+ "consider adding a derive" ,
1490+ format ! ( "#[derive(Default)]\n " ) ,
1491+ Applicability :: MaybeIncorrect ,
1492+ ) ;
13751493 } else {
13761494 err. span_help (
13771495 head_span,
0 commit comments