@@ -4,7 +4,7 @@ use rustc_ast::ptr::P;
4
4
use rustc_ast:: visit:: { self , Visitor } ;
5
5
use rustc_ast:: { self as ast, Crate , ItemKind , ModKind , NodeId , Path , CRATE_NODE_ID } ;
6
6
use rustc_ast_pretty:: pprust;
7
- use rustc_data_structures:: fx:: FxHashSet ;
7
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
8
8
use rustc_errors:: {
9
9
pluralize, Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed , MultiSpan ,
10
10
} ;
@@ -1016,6 +1016,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
1016
1016
parent_scope,
1017
1017
false ,
1018
1018
false ,
1019
+ None ,
1019
1020
) {
1020
1021
suggestions. extend (
1021
1022
ext. helper_attrs
@@ -1331,15 +1332,40 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
1331
1332
macro_kind : MacroKind ,
1332
1333
parent_scope : & ParentScope < ' a > ,
1333
1334
ident : Ident ,
1335
+ sugg_span : Option < Span > ,
1334
1336
) {
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
+
1335
1359
let is_expected = & |res : Res | res. macro_kind ( ) == Some ( macro_kind) ;
1336
1360
let suggestion = self . early_lookup_typo_candidate (
1337
1361
ScopeSet :: Macro ( macro_kind) ,
1338
1362
parent_scope,
1339
1363
ident,
1340
1364
is_expected,
1341
1365
) ;
1342
- self . add_typo_suggestion ( err, suggestion, ident. span ) ;
1366
+ if !self . add_typo_suggestion ( err, suggestion, ident. span ) {
1367
+ self . add_derive_for_attribute ( err, sugg_span, ident, parent_scope) ;
1368
+ }
1343
1369
1344
1370
let import_suggestions =
1345
1371
self . lookup_import_candidates ( ident, Namespace :: MacroNS , parent_scope, is_expected) ;
@@ -1364,14 +1390,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
1364
1390
err. help ( "have you added the `#[macro_use]` on the module/import?" ) ;
1365
1391
return ;
1366
1392
}
1393
+
1367
1394
if ident. name == kw:: Default
1368
1395
&& let ModuleKind :: Def ( DefKind :: Enum , def_id, _) = parent_scope. module . kind
1369
1396
{
1370
1397
let span = self . def_span ( def_id) ;
1371
1398
let source_map = self . tcx . sess . source_map ( ) ;
1372
1399
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 ) ;
1400
+ if let Ok ( _) = source_map. span_to_snippet ( head_span) {
1401
+ err. span_suggestion (
1402
+ head_span. shrink_to_lo ( ) ,
1403
+ "consider adding a derive" ,
1404
+ format ! ( "#[derive(Default)]\n " ) ,
1405
+ Applicability :: MaybeIncorrect ,
1406
+ ) ;
1375
1407
} else {
1376
1408
err. span_help (
1377
1409
head_span,
@@ -1494,6 +1526,102 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
1494
1526
true
1495
1527
}
1496
1528
1529
+ fn add_derive_for_attribute (
1530
+ & self ,
1531
+ err : & mut Diagnostic ,
1532
+ sugg_span : Option < Span > ,
1533
+ ident : Ident ,
1534
+ parent_scope : & ParentScope < ' _ > ,
1535
+ ) {
1536
+ // FIXME: this only works if the macro that has the helper_attr has already
1537
+ // been imported.
1538
+ let mut derives = vec ! [ ] ;
1539
+ let mut all_attrs: FxHashMap < Symbol , Vec < _ > > = FxHashMap :: default ( ) ;
1540
+ for ( def_id, data) in & self . macro_map {
1541
+ for helper_attr in & data. ext . helper_attrs {
1542
+ let item_name = self . tcx . item_name ( * def_id) ;
1543
+ all_attrs. entry ( * helper_attr) . or_default ( ) . push ( item_name) ;
1544
+ if helper_attr == & ident. name {
1545
+ // FIXME: we should also do Levenshtein distance checks here.
1546
+ derives. push ( item_name) ;
1547
+ }
1548
+ }
1549
+ }
1550
+ let kind = MacroKind :: Derive . descr ( ) ;
1551
+ if !derives. is_empty ( ) {
1552
+ derives. sort ( ) ;
1553
+ derives. dedup ( ) ;
1554
+ let msg = match & derives[ ..] {
1555
+ [ derive] => format ! ( " `{derive}`" ) ,
1556
+ [ start @ .., last] => format ! (
1557
+ "s {} and `{last}`" ,
1558
+ start. iter( ) . map( |d| format!( "`{d}`" ) ) . collect:: <Vec <_>>( ) . join( ", " )
1559
+ ) ,
1560
+ [ ] => unreachable ! ( "we checked for this to be non-empty 10 lines above!?" ) ,
1561
+ } ;
1562
+ let msg = format ! (
1563
+ "`{}` is an attribute that can be used by the {kind}{msg}, you might be missing a \
1564
+ `derive` attribute",
1565
+ ident. name,
1566
+ ) ;
1567
+ let sugg_span = if let ModuleKind :: Def ( DefKind :: Enum , id, _) = parent_scope. module . kind
1568
+ {
1569
+ let span = self . def_span ( id) ;
1570
+ if span. from_expansion ( ) {
1571
+ None
1572
+ } else {
1573
+ // For enum variants, `sugg_span` is empty, but we can get the `enum`'s `Span`.
1574
+ Some ( span. shrink_to_lo ( ) )
1575
+ }
1576
+ } else {
1577
+ // For items, this `Span` will be populated, everything else it'll be `None`.
1578
+ sugg_span
1579
+ } ;
1580
+ match sugg_span {
1581
+ Some ( span) => {
1582
+ err. span_suggestion_verbose (
1583
+ span,
1584
+ & msg,
1585
+ format ! (
1586
+ "#[derive({})]\n " ,
1587
+ derives
1588
+ . iter( )
1589
+ . map( |d| d. to_string( ) )
1590
+ . collect:: <Vec <String >>( )
1591
+ . join( ", " )
1592
+ ) ,
1593
+ Applicability :: MaybeIncorrect ,
1594
+ ) ;
1595
+ }
1596
+ None => {
1597
+ err. note ( & msg) ;
1598
+ }
1599
+ }
1600
+ } else {
1601
+ let all_attr_names: Vec < Symbol > = all_attrs. keys ( ) . cloned ( ) . collect ( ) ;
1602
+ if let Some ( best_match) = find_best_match_for_name ( & all_attr_names, ident. name , None )
1603
+ && let Some ( macros) = all_attrs. get ( & best_match)
1604
+ && !macros. is_empty ( )
1605
+ {
1606
+ let msg = match & macros[ ..] {
1607
+ [ ] => unreachable ! ( "we checked above in the if-let" ) ,
1608
+ [ name] => format ! ( " `{name}` accepts" ) ,
1609
+ [ start @ .., end] => format ! (
1610
+ "s {} and `{end}` accept" ,
1611
+ start. iter( ) . map( |m| format!( "`{m}`" ) ) . collect:: <Vec <_>>( ) . join( ", " ) ,
1612
+ ) ,
1613
+ } ;
1614
+ let msg = format ! ( "the {kind}{msg} the similarly named `{best_match}` attribute" ) ;
1615
+ err. span_suggestion_verbose (
1616
+ ident. span ,
1617
+ & msg,
1618
+ best_match,
1619
+ Applicability :: MaybeIncorrect ,
1620
+ ) ;
1621
+ }
1622
+ }
1623
+ }
1624
+
1497
1625
fn binding_description ( & self , b : & NameBinding < ' _ > , ident : Ident , from_prelude : bool ) -> String {
1498
1626
let res = b. res ( ) ;
1499
1627
if b. span . is_dummy ( ) || !self . tcx . sess . source_map ( ) . is_span_accessible ( b. span ) {
0 commit comments