@@ -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 } ;
@@ -2442,31 +2442,73 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
2442
2442
def_id : DefId ,
2443
2443
span : Span ,
2444
2444
) {
2445
- let Some ( variants ) = self . collect_enum_ctors ( def_id) else {
2445
+ let Some ( variant_ctors ) = self . collect_enum_ctors ( def_id) else {
2446
2446
err. note ( "you might have meant to use one of the enum's variants" ) ;
2447
2447
return ;
2448
2448
} ;
2449
2449
2450
- let suggest_only_tuple_variants =
2451
- matches ! ( source, PathSource :: TupleStruct ( ..) ) || source. is_call ( ) ;
2452
- if suggest_only_tuple_variants {
2450
+ // If the expression is a field-access or method-call, try to find a variant with the field/method name
2451
+ // that could have been intended, and suggest replacing the `.` with `::`.
2452
+ // Otherwise, suggest adding `::VariantName` after the enum;
2453
+ // and if the expression is call-like, only suggest tuple variants.
2454
+ let ( suggest_path_sep_dot_span, suggest_only_tuple_variants) = match source {
2455
+ // `Type(a, b)` in a pattern, only suggest adding a tuple variant after `Type`.
2456
+ PathSource :: TupleStruct ( ..) => ( None , true ) ,
2457
+ PathSource :: Expr ( Some ( expr) ) => match & expr. kind {
2458
+ // `Type(a, b)`, only suggest adding a tuple variant after `Type`.
2459
+ ExprKind :: Call ( ..) => ( None , true ) ,
2460
+ // `Type.Foo(a, b)`, suggest replacing `.` -> `::` if variant `Foo` exists and is a tuple variant,
2461
+ // otherwise suggest adding a variant after `Type`.
2462
+ ExprKind :: MethodCall ( box MethodCall {
2463
+ receiver,
2464
+ span,
2465
+ seg : PathSegment { ident, .. } ,
2466
+ ..
2467
+ } ) => {
2468
+ let dot_span = receiver. span . between ( * span) ;
2469
+ let found_tuple_variant = variant_ctors. iter ( ) . any ( |( path, _, ctor_kind) | {
2470
+ * ctor_kind == CtorKind :: Fn
2471
+ && path. segments . last ( ) . is_some_and ( |seg| seg. ident == * ident)
2472
+ } ) ;
2473
+ ( found_tuple_variant. then_some ( dot_span) , false )
2474
+ }
2475
+ // `Type.Foo`, suggest replacing `.` -> `::` if variant `Foo` exists and is a unit or tuple variant,
2476
+ // otherwise suggest adding a variant after `Type`.
2477
+ ExprKind :: Field ( base, ident) => {
2478
+ let dot_span = base. span . between ( ident. span ) ;
2479
+ let found_tuple_or_unit_variant = variant_ctors. iter ( ) . any ( |( path, ..) | {
2480
+ path. segments . last ( ) . is_some_and ( |seg| seg. ident == * ident)
2481
+ } ) ;
2482
+ ( found_tuple_or_unit_variant. then_some ( dot_span) , false )
2483
+ }
2484
+ _ => ( None , false ) ,
2485
+ } ,
2486
+ _ => ( None , false ) ,
2487
+ } ;
2488
+
2489
+ if let Some ( dot_span) = suggest_path_sep_dot_span {
2490
+ err. span_suggestion_verbose (
2491
+ dot_span,
2492
+ "use the path separator to refer to a variant" ,
2493
+ "::" ,
2494
+ Applicability :: MaybeIncorrect ,
2495
+ ) ;
2496
+ } else if suggest_only_tuple_variants {
2453
2497
// Suggest only tuple variants regardless of whether they have fields and do not
2454
2498
// suggest path with added parentheses.
2455
- let mut suggestable_variants = variants
2499
+ let mut suggestable_variants = variant_ctors
2456
2500
. iter ( )
2457
2501
. filter ( |( .., kind) | * kind == CtorKind :: Fn )
2458
2502
. map ( |( variant, ..) | path_names_to_string ( variant) )
2459
2503
. collect :: < Vec < _ > > ( ) ;
2460
2504
suggestable_variants. sort ( ) ;
2461
2505
2462
- let non_suggestable_variant_count = variants . len ( ) - suggestable_variants. len ( ) ;
2506
+ let non_suggestable_variant_count = variant_ctors . len ( ) - suggestable_variants. len ( ) ;
2463
2507
2464
- let source_msg = if source. is_call ( ) {
2465
- "to construct"
2466
- } else if matches ! ( source, PathSource :: TupleStruct ( ..) ) {
2508
+ let source_msg = if matches ! ( source, PathSource :: TupleStruct ( ..) ) {
2467
2509
"to match against"
2468
2510
} else {
2469
- unreachable ! ( )
2511
+ "to construct"
2470
2512
} ;
2471
2513
2472
2514
if !suggestable_variants. is_empty ( ) {
@@ -2485,7 +2527,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
2485
2527
}
2486
2528
2487
2529
// If the enum has no tuple variants..
2488
- if non_suggestable_variant_count == variants . len ( ) {
2530
+ if non_suggestable_variant_count == variant_ctors . len ( ) {
2489
2531
err. help ( format ! ( "the enum has no tuple variants {source_msg}" ) ) ;
2490
2532
}
2491
2533
@@ -2508,7 +2550,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
2508
2550
}
2509
2551
} ;
2510
2552
2511
- let mut suggestable_variants = variants
2553
+ let mut suggestable_variants = variant_ctors
2512
2554
. iter ( )
2513
2555
. filter ( |( _, def_id, kind) | !needs_placeholder ( * def_id, * kind) )
2514
2556
. map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
@@ -2535,7 +2577,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
2535
2577
) ;
2536
2578
}
2537
2579
2538
- let mut suggestable_variants_with_placeholders = variants
2580
+ let mut suggestable_variants_with_placeholders = 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) )
0 commit comments