@@ -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 } ;
@@ -2469,31 +2469,73 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
2469
2469
def_id : DefId ,
2470
2470
span : Span ,
2471
2471
) {
2472
- let Some ( variants ) = self . collect_enum_ctors ( def_id) else {
2472
+ let Some ( variant_ctors ) = self . collect_enum_ctors ( def_id) else {
2473
2473
err. note ( "you might have meant to use one of the enum's variants" ) ;
2474
2474
return ;
2475
2475
} ;
2476
2476
2477
- let suggest_only_tuple_variants =
2478
- matches ! ( source, PathSource :: TupleStruct ( ..) ) || source. is_call ( ) ;
2479
- if suggest_only_tuple_variants {
2477
+ // If the expression is a field-access or method-call, try to find a variant with the field/method name
2478
+ // that could have been intended, and suggest replacing the `.` with `::`.
2479
+ // Otherwise, suggest adding `::VariantName` after the enum;
2480
+ // and if the expression is call-like, only suggest tuple variants.
2481
+ let ( suggest_path_sep_dot_span, suggest_only_tuple_variants) = match source {
2482
+ // `Type(a, b)` in a pattern, only suggest adding a tuple variant after `Type`.
2483
+ PathSource :: TupleStruct ( ..) => ( None , true ) ,
2484
+ PathSource :: Expr ( Some ( expr) ) => match & expr. kind {
2485
+ // `Type(a, b)`, only suggest adding a tuple variant after `Type`.
2486
+ ExprKind :: Call ( ..) => ( None , true ) ,
2487
+ // `Type.Foo(a, b)`, suggest replacing `.` -> `::` if variant `Foo` exists and is a tuple variant,
2488
+ // otherwise suggest adding a variant after `Type`.
2489
+ ExprKind :: MethodCall ( box MethodCall {
2490
+ receiver,
2491
+ span,
2492
+ seg : PathSegment { ident, .. } ,
2493
+ ..
2494
+ } ) => {
2495
+ let dot_span = receiver. span . between ( * span) ;
2496
+ let found_tuple_variant = variant_ctors. iter ( ) . any ( |( path, _, ctor_kind) | {
2497
+ * ctor_kind == CtorKind :: Fn
2498
+ && path. segments . last ( ) . is_some_and ( |seg| seg. ident == * ident)
2499
+ } ) ;
2500
+ ( found_tuple_variant. then_some ( dot_span) , false )
2501
+ }
2502
+ // `Type.Foo`, suggest replacing `.` -> `::` if variant `Foo` exists and is a unit or tuple variant,
2503
+ // otherwise suggest adding a variant after `Type`.
2504
+ ExprKind :: Field ( base, ident) => {
2505
+ let dot_span = base. span . between ( ident. span ) ;
2506
+ let found_tuple_or_unit_variant = variant_ctors. iter ( ) . any ( |( path, ..) | {
2507
+ path. segments . last ( ) . is_some_and ( |seg| seg. ident == * ident)
2508
+ } ) ;
2509
+ ( found_tuple_or_unit_variant. then_some ( dot_span) , false )
2510
+ }
2511
+ _ => ( None , false ) ,
2512
+ } ,
2513
+ _ => ( None , false ) ,
2514
+ } ;
2515
+
2516
+ if let Some ( dot_span) = suggest_path_sep_dot_span {
2517
+ err. span_suggestion_verbose (
2518
+ dot_span,
2519
+ "use the path separator to refer to a variant" ,
2520
+ "::" ,
2521
+ Applicability :: MaybeIncorrect ,
2522
+ ) ;
2523
+ } else if suggest_only_tuple_variants {
2480
2524
// Suggest only tuple variants regardless of whether they have fields and do not
2481
2525
// suggest path with added parentheses.
2482
- let mut suggestable_variants = variants
2526
+ let mut suggestable_variants = variant_ctors
2483
2527
. iter ( )
2484
2528
. filter ( |( .., kind) | * kind == CtorKind :: Fn )
2485
2529
. map ( |( variant, ..) | path_names_to_string ( variant) )
2486
2530
. collect :: < Vec < _ > > ( ) ;
2487
2531
suggestable_variants. sort ( ) ;
2488
2532
2489
- let non_suggestable_variant_count = variants . len ( ) - suggestable_variants. len ( ) ;
2533
+ let non_suggestable_variant_count = variant_ctors . len ( ) - suggestable_variants. len ( ) ;
2490
2534
2491
- let source_msg = if source. is_call ( ) {
2492
- "to construct"
2493
- } else if matches ! ( source, PathSource :: TupleStruct ( ..) ) {
2535
+ let source_msg = if matches ! ( source, PathSource :: TupleStruct ( ..) ) {
2494
2536
"to match against"
2495
2537
} else {
2496
- unreachable ! ( )
2538
+ "to construct"
2497
2539
} ;
2498
2540
2499
2541
if !suggestable_variants. is_empty ( ) {
@@ -2512,7 +2554,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
2512
2554
}
2513
2555
2514
2556
// If the enum has no tuple variants..
2515
- if non_suggestable_variant_count == variants . len ( ) {
2557
+ if non_suggestable_variant_count == variant_ctors . len ( ) {
2516
2558
err. help ( format ! ( "the enum has no tuple variants {source_msg}" ) ) ;
2517
2559
}
2518
2560
@@ -2535,7 +2577,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
2535
2577
}
2536
2578
} ;
2537
2579
2538
- let mut suggestable_variants = variants
2580
+ let mut suggestable_variants = variant_ctors
2539
2581
. iter ( )
2540
2582
. filter ( |( _, def_id, kind) | !needs_placeholder ( * def_id, * kind) )
2541
2583
. map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
@@ -2562,7 +2604,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
2562
2604
) ;
2563
2605
}
2564
2606
2565
- let mut suggestable_variants_with_placeholders = variants
2607
+ let mut suggestable_variants_with_placeholders = variant_ctors
2566
2608
. iter ( )
2567
2609
. filter ( |( _, def_id, kind) | needs_placeholder ( * def_id, * kind) )
2568
2610
. map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
0 commit comments