@@ -8,7 +8,7 @@ use rustc_ast::ptr::P;
88use rustc_ast:: visit:: { FnCtxt , FnKind , LifetimeCtxt , Visitor , walk_ty} ;
99use rustc_ast:: {
1010 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 ,
1212} ;
1313use rustc_ast_pretty:: pprust:: where_bound_predicate_to_string;
1414use rustc_data_structures:: fx:: { FxHashSet , FxIndexSet } ;
@@ -2442,31 +2442,73 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
24422442 def_id : DefId ,
24432443 span : Span ,
24442444 ) {
2445- let Some ( variants ) = self . collect_enum_ctors ( def_id) else {
2445+ let Some ( variant_ctors ) = self . collect_enum_ctors ( def_id) else {
24462446 err. note ( "you might have meant to use one of the enum's variants" ) ;
24472447 return ;
24482448 } ;
24492449
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 {
24532497 // Suggest only tuple variants regardless of whether they have fields and do not
24542498 // suggest path with added parentheses.
2455- let mut suggestable_variants = variants
2499+ let mut suggestable_variants = variant_ctors
24562500 . iter ( )
24572501 . filter ( |( .., kind) | * kind == CtorKind :: Fn )
24582502 . map ( |( variant, ..) | path_names_to_string ( variant) )
24592503 . collect :: < Vec < _ > > ( ) ;
24602504 suggestable_variants. sort ( ) ;
24612505
2462- let non_suggestable_variant_count = variants . len ( ) - suggestable_variants. len ( ) ;
2506+ let non_suggestable_variant_count = variant_ctors . len ( ) - suggestable_variants. len ( ) ;
24632507
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 ( ..) ) {
24672509 "to match against"
24682510 } else {
2469- unreachable ! ( )
2511+ "to construct"
24702512 } ;
24712513
24722514 if !suggestable_variants. is_empty ( ) {
@@ -2485,7 +2527,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
24852527 }
24862528
24872529 // If the enum has no tuple variants..
2488- if non_suggestable_variant_count == variants . len ( ) {
2530+ if non_suggestable_variant_count == variant_ctors . len ( ) {
24892531 err. help ( format ! ( "the enum has no tuple variants {source_msg}" ) ) ;
24902532 }
24912533
@@ -2508,7 +2550,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25082550 }
25092551 } ;
25102552
2511- let mut suggestable_variants = variants
2553+ let mut suggestable_variants = variant_ctors
25122554 . iter ( )
25132555 . filter ( |( _, def_id, kind) | !needs_placeholder ( * def_id, * kind) )
25142556 . map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
@@ -2535,7 +2577,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25352577 ) ;
25362578 }
25372579
2538- let mut suggestable_variants_with_placeholders = variants
2580+ let mut suggestable_variants_with_placeholders = variant_ctors
25392581 . iter ( )
25402582 . filter ( |( _, def_id, kind) | needs_placeholder ( * def_id, * kind) )
25412583 . map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
0 commit comments