@@ -1054,6 +1054,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
1054
1054
false ,
1055
1055
false ,
1056
1056
None ,
1057
+ None ,
1057
1058
) else {
1058
1059
continue ;
1059
1060
} ;
@@ -1482,15 +1483,45 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
1482
1483
parent_scope : & ParentScope < ' ra > ,
1483
1484
ident : Ident ,
1484
1485
krate : & Crate ,
1486
+ sugg_span : Option < Span > ,
1485
1487
) {
1488
+ // Bring imported but unused `derive` macros into `macro_map` so we ensure they can be used
1489
+ // for suggestions.
1490
+ self . visit_scopes (
1491
+ ScopeSet :: Macro ( MacroKind :: Derive ) ,
1492
+ & parent_scope,
1493
+ ident. span . ctxt ( ) ,
1494
+ |this, scope, _use_prelude, _ctxt| {
1495
+ let Scope :: Module ( m, _) = scope else {
1496
+ return None ;
1497
+ } ;
1498
+ for ( _, resolution) in this. resolutions ( m) . borrow ( ) . iter ( ) {
1499
+ let Some ( binding) = resolution. borrow ( ) . binding else {
1500
+ continue ;
1501
+ } ;
1502
+ let Res :: Def ( DefKind :: Macro ( MacroKind :: Derive | MacroKind :: Attr ) , def_id) =
1503
+ binding. res ( )
1504
+ else {
1505
+ continue ;
1506
+ } ;
1507
+ // By doing this all *imported* macros get added to the `macro_map` even if they
1508
+ // are *unused*, which makes the later suggestions find them and work.
1509
+ let _ = this. get_macro_by_def_id ( def_id) ;
1510
+ }
1511
+ None :: < ( ) >
1512
+ } ,
1513
+ ) ;
1514
+
1486
1515
let is_expected = & |res : Res | res. macro_kind ( ) == Some ( macro_kind) ;
1487
1516
let suggestion = self . early_lookup_typo_candidate (
1488
1517
ScopeSet :: Macro ( macro_kind) ,
1489
1518
parent_scope,
1490
1519
ident,
1491
1520
is_expected,
1492
1521
) ;
1493
- self . add_typo_suggestion ( err, suggestion, ident. span ) ;
1522
+ if !self . add_typo_suggestion ( err, suggestion, ident. span ) {
1523
+ self . detect_derive_attribute ( err, ident, parent_scope, sugg_span) ;
1524
+ }
1494
1525
1495
1526
let import_suggestions =
1496
1527
self . lookup_import_candidates ( ident, Namespace :: MacroNS , parent_scope, is_expected) ;
@@ -1623,6 +1654,110 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
1623
1654
}
1624
1655
}
1625
1656
1657
+ /// Given an attribute macro that failed to be resolved, look for `derive` macros that could
1658
+ /// provide it, either as-is or with small typos.
1659
+ fn detect_derive_attribute (
1660
+ & self ,
1661
+ err : & mut Diag < ' _ > ,
1662
+ ident : Ident ,
1663
+ parent_scope : & ParentScope < ' ra > ,
1664
+ sugg_span : Option < Span > ,
1665
+ ) {
1666
+ // Find all of the `derive`s in scope and collect their corresponding declared
1667
+ // attributes.
1668
+ // FIXME: this only works if the crate that owns the macro that has the helper_attr
1669
+ // has already been imported.
1670
+ let mut derives = vec ! [ ] ;
1671
+ let mut all_attrs: FxHashMap < Symbol , Vec < _ > > = FxHashMap :: default ( ) ;
1672
+ // We're collecting these in a hashmap, and handle ordering the output further down.
1673
+ #[ allow( rustc:: potential_query_instability) ]
1674
+ for ( def_id, data) in & self . macro_map {
1675
+ for helper_attr in & data. ext . helper_attrs {
1676
+ let item_name = self . tcx . item_name ( * def_id) ;
1677
+ all_attrs. entry ( * helper_attr) . or_default ( ) . push ( item_name) ;
1678
+ if helper_attr == & ident. name {
1679
+ derives. push ( item_name) ;
1680
+ }
1681
+ }
1682
+ }
1683
+ let kind = MacroKind :: Derive . descr ( ) ;
1684
+ if !derives. is_empty ( ) {
1685
+ // We found an exact match for the missing attribute in a `derive` macro. Suggest it.
1686
+ derives. sort ( ) ;
1687
+ derives. dedup ( ) ;
1688
+ let msg = match & derives[ ..] {
1689
+ [ derive] => format ! ( " `{derive}`" ) ,
1690
+ [ start @ .., last] => format ! (
1691
+ "s {} and `{last}`" ,
1692
+ start. iter( ) . map( |d| format!( "`{d}`" ) ) . collect:: <Vec <_>>( ) . join( ", " )
1693
+ ) ,
1694
+ [ ] => unreachable ! ( "we checked for this to be non-empty 10 lines above!?" ) ,
1695
+ } ;
1696
+ let msg = format ! (
1697
+ "`{}` is an attribute that can be used by the {kind}{msg}, you might be \
1698
+ missing a `derive` attribute",
1699
+ ident. name,
1700
+ ) ;
1701
+ let sugg_span = if let ModuleKind :: Def ( DefKind :: Enum , id, _) = parent_scope. module . kind
1702
+ {
1703
+ let span = self . def_span ( id) ;
1704
+ if span. from_expansion ( ) {
1705
+ None
1706
+ } else {
1707
+ // For enum variants sugg_span is empty but we can get the enum's Span.
1708
+ Some ( span. shrink_to_lo ( ) )
1709
+ }
1710
+ } else {
1711
+ // For items this `Span` will be populated, everything else it'll be None.
1712
+ sugg_span
1713
+ } ;
1714
+ match sugg_span {
1715
+ Some ( span) => {
1716
+ err. span_suggestion_verbose (
1717
+ span,
1718
+ msg,
1719
+ format ! (
1720
+ "#[derive({})]\n " ,
1721
+ derives
1722
+ . iter( )
1723
+ . map( |d| d. to_string( ) )
1724
+ . collect:: <Vec <String >>( )
1725
+ . join( ", " )
1726
+ ) ,
1727
+ Applicability :: MaybeIncorrect ,
1728
+ ) ;
1729
+ }
1730
+ None => {
1731
+ err. note ( msg) ;
1732
+ }
1733
+ }
1734
+ } else {
1735
+ // We didn't find an exact match. Look for close matches. If any, suggest fixing typo.
1736
+ #[ allow( rustc:: potential_query_instability) ] // We're immediately sorting these.
1737
+ let mut all_attr_names: Vec < Symbol > = all_attrs. keys ( ) . cloned ( ) . collect ( ) ;
1738
+ all_attr_names. sort ( ) ;
1739
+ if let Some ( best_match) = find_best_match_for_name ( & all_attr_names, ident. name , None )
1740
+ && let Some ( macros) = all_attrs. get ( & best_match)
1741
+ {
1742
+ let msg = match & macros[ ..] {
1743
+ [ ] => return ,
1744
+ [ name] => format ! ( " `{name}` accepts" ) ,
1745
+ [ start @ .., end] => format ! (
1746
+ "s {} and `{end}` accept" ,
1747
+ start. iter( ) . map( |m| format!( "`{m}`" ) ) . collect:: <Vec <_>>( ) . join( ", " ) ,
1748
+ ) ,
1749
+ } ;
1750
+ let msg = format ! ( "the {kind}{msg} the similarly named `{best_match}` attribute" ) ;
1751
+ err. span_suggestion_verbose (
1752
+ ident. span ,
1753
+ msg,
1754
+ best_match,
1755
+ Applicability :: MaybeIncorrect ,
1756
+ ) ;
1757
+ }
1758
+ }
1759
+ }
1760
+
1626
1761
pub ( crate ) fn add_typo_suggestion (
1627
1762
& self ,
1628
1763
err : & mut Diag < ' _ > ,
0 commit comments