@@ -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 } ;
@@ -2469,31 +2469,73 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
24692469 def_id : DefId ,
24702470 span : Span ,
24712471 ) {
2472- let Some ( variants ) = self . collect_enum_ctors ( def_id) else {
2472+ let Some ( variant_ctors ) = self . collect_enum_ctors ( def_id) else {
24732473 err. note ( "you might have meant to use one of the enum's variants" ) ;
24742474 return ;
24752475 } ;
24762476
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 {
24802524 // Suggest only tuple variants regardless of whether they have fields and do not
24812525 // suggest path with added parentheses.
2482- let mut suggestable_variants = variants
2526+ let mut suggestable_variants = variant_ctors
24832527 . iter ( )
24842528 . filter ( |( .., kind) | * kind == CtorKind :: Fn )
24852529 . map ( |( variant, ..) | path_names_to_string ( variant) )
24862530 . collect :: < Vec < _ > > ( ) ;
24872531 suggestable_variants. sort ( ) ;
24882532
2489- let non_suggestable_variant_count = variants . len ( ) - suggestable_variants. len ( ) ;
2533+ let non_suggestable_variant_count = variant_ctors . len ( ) - suggestable_variants. len ( ) ;
24902534
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 ( ..) ) {
24942536 "to match against"
24952537 } else {
2496- unreachable ! ( )
2538+ "to construct"
24972539 } ;
24982540
24992541 if !suggestable_variants. is_empty ( ) {
@@ -2512,7 +2554,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25122554 }
25132555
25142556 // If the enum has no tuple variants..
2515- if non_suggestable_variant_count == variants . len ( ) {
2557+ if non_suggestable_variant_count == variant_ctors . len ( ) {
25162558 err. help ( format ! ( "the enum has no tuple variants {source_msg}" ) ) ;
25172559 }
25182560
@@ -2535,7 +2577,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25352577 }
25362578 } ;
25372579
2538- let mut suggestable_variants = variants
2580+ let mut suggestable_variants = variant_ctors
25392581 . iter ( )
25402582 . filter ( |( _, def_id, kind) | !needs_placeholder ( * def_id, * kind) )
25412583 . map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
@@ -2562,7 +2604,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25622604 ) ;
25632605 }
25642606
2565- let mut suggestable_variants_with_placeholders = variants
2607+ let mut suggestable_variants_with_placeholders = variant_ctors
25662608 . iter ( )
25672609 . filter ( |( _, def_id, kind) | needs_placeholder ( * def_id, * kind) )
25682610 . map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
0 commit comments