@@ -8,7 +8,7 @@ use rustc_ast::ptr::P;
8
8
use rustc_ast:: visit:: { FnCtxt , FnKind , LifetimeCtxt , Visitor , walk_ty} ;
9
9
use rustc_ast:: {
10
10
self as ast, AssocItemKind , DUMMY_NODE_ID , Expr , ExprKind , GenericParam , GenericParamKind ,
11
- Item , ItemKind , MethodCall , NodeId , Path , Ty , TyKind ,
11
+ Item , ItemKind , MethodCall , NodeId , Path , PathSegment , Ty , TyKind ,
12
12
} ;
13
13
use rustc_ast_pretty:: pprust:: where_bound_predicate_to_string;
14
14
use rustc_data_structures:: fx:: { FxHashSet , FxIndexSet } ;
@@ -2448,31 +2448,73 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
2448
2448
def_id : DefId ,
2449
2449
span : Span ,
2450
2450
) {
2451
- let Some ( variants ) = self . collect_enum_ctors ( def_id) else {
2451
+ let Some ( variant_ctors ) = self . collect_enum_ctors ( def_id) else {
2452
2452
err. note ( "you might have meant to use one of the enum's variants" ) ;
2453
2453
return ;
2454
2454
} ;
2455
2455
2456
- let suggest_only_tuple_variants =
2457
- matches ! ( source, PathSource :: TupleStruct ( ..) ) || source. is_call ( ) ;
2458
- if suggest_only_tuple_variants {
2456
+ // If the expression is a field-access or method-call, try to find a variant with the field/method name
2457
+ // that could have been intended, and suggest replacing the `.` with `::`.
2458
+ // Otherwise, suggest adding `::VariantName` after the enum;
2459
+ // and if the expression is call-like, only suggest tuple variants.
2460
+ let ( suggest_path_sep_dot_span, suggest_only_tuple_variants) = match source {
2461
+ // `Type(a, b)` in a pattern, only suggest adding a tuple variant after `Type`.
2462
+ PathSource :: TupleStruct ( ..) => ( None , true ) ,
2463
+ PathSource :: Expr ( Some ( expr) ) => match & expr. kind {
2464
+ // `Type(a, b)`, only suggest adding a tuple variant after `Type`.
2465
+ ExprKind :: Call ( ..) => ( None , true ) ,
2466
+ // `Type.Foo(a, b)`, suggest replacing `.` -> `::` if variant `Foo` exists and is a tuple variant,
2467
+ // otherwise suggest adding a variant after `Type`.
2468
+ ExprKind :: MethodCall ( box MethodCall {
2469
+ receiver,
2470
+ span,
2471
+ seg : PathSegment { ident, .. } ,
2472
+ ..
2473
+ } ) => {
2474
+ let dot_span = receiver. span . between ( * span) ;
2475
+ let found_tuple_variant = variant_ctors. iter ( ) . any ( |( path, _, ctor_kind) | {
2476
+ * ctor_kind == CtorKind :: Fn
2477
+ && path. segments . last ( ) . is_some_and ( |seg| seg. ident == * ident)
2478
+ } ) ;
2479
+ ( found_tuple_variant. then_some ( dot_span) , false )
2480
+ }
2481
+ // `Type.Foo`, suggest replacing `.` -> `::` if variant `Foo` exists and is a unit or tuple variant,
2482
+ // otherwise suggest adding a variant after `Type`.
2483
+ ExprKind :: Field ( base, ident) => {
2484
+ let dot_span = base. span . between ( ident. span ) ;
2485
+ let found_tuple_or_unit_variant = variant_ctors. iter ( ) . any ( |( path, ..) | {
2486
+ path. segments . last ( ) . is_some_and ( |seg| seg. ident == * ident)
2487
+ } ) ;
2488
+ ( found_tuple_or_unit_variant. then_some ( dot_span) , false )
2489
+ }
2490
+ _ => ( None , false ) ,
2491
+ } ,
2492
+ _ => ( None , false ) ,
2493
+ } ;
2494
+
2495
+ if let Some ( dot_span) = suggest_path_sep_dot_span {
2496
+ err. span_suggestion_verbose (
2497
+ dot_span,
2498
+ "use the path separator to refer to a variant" ,
2499
+ "::" ,
2500
+ Applicability :: MaybeIncorrect ,
2501
+ ) ;
2502
+ } else if suggest_only_tuple_variants {
2459
2503
// Suggest only tuple variants regardless of whether they have fields and do not
2460
2504
// suggest path with added parentheses.
2461
- let mut suggestable_variants = variants
2505
+ let mut suggestable_variants = variant_ctors
2462
2506
. iter ( )
2463
2507
. filter ( |( .., kind) | * kind == CtorKind :: Fn )
2464
2508
. map ( |( variant, ..) | path_names_to_string ( variant) )
2465
2509
. collect :: < Vec < _ > > ( ) ;
2466
2510
suggestable_variants. sort ( ) ;
2467
2511
2468
- let non_suggestable_variant_count = variants . len ( ) - suggestable_variants. len ( ) ;
2512
+ let non_suggestable_variant_count = variant_ctors . len ( ) - suggestable_variants. len ( ) ;
2469
2513
2470
- let source_msg = if source. is_call ( ) {
2471
- "to construct"
2472
- } else if matches ! ( source, PathSource :: TupleStruct ( ..) ) {
2514
+ let source_msg = if matches ! ( source, PathSource :: TupleStruct ( ..) ) {
2473
2515
"to match against"
2474
2516
} else {
2475
- unreachable ! ( )
2517
+ "to construct"
2476
2518
} ;
2477
2519
2478
2520
if !suggestable_variants. is_empty ( ) {
@@ -2491,7 +2533,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
2491
2533
}
2492
2534
2493
2535
// If the enum has no tuple variants..
2494
- if non_suggestable_variant_count == variants . len ( ) {
2536
+ if non_suggestable_variant_count == variant_ctors . len ( ) {
2495
2537
err. help ( format ! ( "the enum has no tuple variants {source_msg}" ) ) ;
2496
2538
}
2497
2539
@@ -2514,7 +2556,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
2514
2556
}
2515
2557
} ;
2516
2558
2517
- let mut suggestable_variants = variants
2559
+ let mut suggestable_variants = variant_ctors
2518
2560
. iter ( )
2519
2561
. filter ( |( _, def_id, kind) | !needs_placeholder ( * def_id, * kind) )
2520
2562
. map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
@@ -2541,7 +2583,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
2541
2583
) ;
2542
2584
}
2543
2585
2544
- let mut suggestable_variants_with_placeholders = variants
2586
+ let mut suggestable_variants_with_placeholders = variant_ctors
2545
2587
. iter ( )
2546
2588
. filter ( |( _, def_id, kind) | needs_placeholder ( * def_id, * kind) )
2547
2589
. map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
0 commit comments