@@ -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 } ;
@@ -2448,31 +2448,73 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
24482448 def_id : DefId ,
24492449 span : Span ,
24502450 ) {
2451- let Some ( variants ) = self . collect_enum_ctors ( def_id) else {
2451+ let Some ( variant_ctors ) = self . collect_enum_ctors ( def_id) else {
24522452 err. note ( "you might have meant to use one of the enum's variants" ) ;
24532453 return ;
24542454 } ;
24552455
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 {
24592503 // Suggest only tuple variants regardless of whether they have fields and do not
24602504 // suggest path with added parentheses.
2461- let mut suggestable_variants = variants
2505+ let mut suggestable_variants = variant_ctors
24622506 . iter ( )
24632507 . filter ( |( .., kind) | * kind == CtorKind :: Fn )
24642508 . map ( |( variant, ..) | path_names_to_string ( variant) )
24652509 . collect :: < Vec < _ > > ( ) ;
24662510 suggestable_variants. sort ( ) ;
24672511
2468- let non_suggestable_variant_count = variants . len ( ) - suggestable_variants. len ( ) ;
2512+ let non_suggestable_variant_count = variant_ctors . len ( ) - suggestable_variants. len ( ) ;
24692513
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 ( ..) ) {
24732515 "to match against"
24742516 } else {
2475- unreachable ! ( )
2517+ "to construct"
24762518 } ;
24772519
24782520 if !suggestable_variants. is_empty ( ) {
@@ -2491,7 +2533,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
24912533 }
24922534
24932535 // If the enum has no tuple variants..
2494- if non_suggestable_variant_count == variants . len ( ) {
2536+ if non_suggestable_variant_count == variant_ctors . len ( ) {
24952537 err. help ( format ! ( "the enum has no tuple variants {source_msg}" ) ) ;
24962538 }
24972539
@@ -2514,7 +2556,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25142556 }
25152557 } ;
25162558
2517- let mut suggestable_variants = variants
2559+ let mut suggestable_variants = variant_ctors
25182560 . iter ( )
25192561 . filter ( |( _, def_id, kind) | !needs_placeholder ( * def_id, * kind) )
25202562 . map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
@@ -2541,7 +2583,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25412583 ) ;
25422584 }
25432585
2544- let mut suggestable_variants_with_placeholders = variants
2586+ let mut suggestable_variants_with_placeholders = variant_ctors
25452587 . iter ( )
25462588 . filter ( |( _, def_id, kind) | needs_placeholder ( * def_id, * kind) )
25472589 . map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
0 commit comments