@@ -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,126 @@ 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
+ // 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
+ }
1343
1455
1344
1456
let import_suggestions =
1345
1457
self . lookup_import_candidates ( ident, Namespace :: MacroNS , parent_scope, is_expected) ;
@@ -1364,14 +1476,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
1364
1476
err. help ( "have you added the `#[macro_use]` on the module/import?" ) ;
1365
1477
return ;
1366
1478
}
1479
+
1367
1480
if ident. name == kw:: Default
1368
1481
&& let ModuleKind :: Def ( DefKind :: Enum , def_id, _) = parent_scope. module . kind
1369
1482
{
1370
1483
let span = self . def_span ( def_id) ;
1371
1484
let source_map = self . tcx . sess . source_map ( ) ;
1372
1485
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
+ ) ;
1375
1493
} else {
1376
1494
err. span_help (
1377
1495
head_span,
0 commit comments