@@ -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 } ;
@@ -1529,7 +1529,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
15291529 Applicability :: MaybeIncorrect ,
15301530 ) ;
15311531 true
1532- } else if kind == DefKind :: Struct
1532+ } else if matches ! ( kind, DefKind :: Struct | DefKind :: TyAlias )
15331533 && let Some ( lhs_source_span) = lhs_span. find_ancestor_inside ( expr. span )
15341534 && let Ok ( snippet) = this. r . tcx . sess . source_map ( ) . span_to_snippet ( lhs_source_span)
15351535 {
@@ -1566,7 +1566,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
15661566 }
15671567 } ;
15681568
1569- let mut bad_struct_syntax_suggestion = |this : & mut Self , def_id : DefId | {
1569+ let bad_struct_syntax_suggestion = |this : & mut Self , err : & mut Diag < ' _ > , def_id : DefId | {
15701570 let ( followed_by_brace, closing_brace) = this. followed_by_brace ( span) ;
15711571
15721572 match source {
@@ -1740,12 +1740,10 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
17401740 }
17411741 }
17421742 (
1743- Res :: Def ( kind @ ( DefKind :: Mod | DefKind :: Trait ) , _) ,
1743+ Res :: Def ( kind @ ( DefKind :: Mod | DefKind :: Trait | DefKind :: TyAlias ) , _) ,
17441744 PathSource :: Expr ( Some ( parent) ) ,
1745- ) => {
1746- if !path_sep ( self , err, parent, kind) {
1747- return false ;
1748- }
1745+ ) if path_sep ( self , err, parent, kind) => {
1746+ return true ;
17491747 }
17501748 (
17511749 Res :: Def ( DefKind :: Enum , def_id) ,
@@ -1777,13 +1775,13 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
17771775 let ( ctor_def, ctor_vis, fields) = if let Some ( struct_ctor) = struct_ctor {
17781776 if let PathSource :: Expr ( Some ( parent) ) = source {
17791777 if let ExprKind :: Field ( ..) | ExprKind :: MethodCall ( ..) = parent. kind {
1780- bad_struct_syntax_suggestion ( self , def_id) ;
1778+ bad_struct_syntax_suggestion ( self , err , def_id) ;
17811779 return true ;
17821780 }
17831781 }
17841782 struct_ctor
17851783 } else {
1786- bad_struct_syntax_suggestion ( self , def_id) ;
1784+ bad_struct_syntax_suggestion ( self , err , def_id) ;
17871785 return true ;
17881786 } ;
17891787
@@ -1861,7 +1859,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
18611859 err. span_label ( span, "constructor is not visible here due to private fields" ) ;
18621860 }
18631861 ( Res :: Def ( DefKind :: Union | DefKind :: Variant , def_id) , _) if ns == ValueNS => {
1864- bad_struct_syntax_suggestion ( self , def_id) ;
1862+ bad_struct_syntax_suggestion ( self , err , def_id) ;
18651863 }
18661864 ( Res :: Def ( DefKind :: Ctor ( _, CtorKind :: Const ) , def_id) , _) if ns == ValueNS => {
18671865 match source {
@@ -2471,31 +2469,73 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
24712469 def_id : DefId ,
24722470 span : Span ,
24732471 ) {
2474- let Some ( variants ) = self . collect_enum_ctors ( def_id) else {
2472+ let Some ( variant_ctors ) = self . collect_enum_ctors ( def_id) else {
24752473 err. note ( "you might have meant to use one of the enum's variants" ) ;
24762474 return ;
24772475 } ;
24782476
2479- let suggest_only_tuple_variants =
2480- matches ! ( source, PathSource :: TupleStruct ( ..) ) || source. is_call ( ) ;
2481- 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 {
24822524 // Suggest only tuple variants regardless of whether they have fields and do not
24832525 // suggest path with added parentheses.
2484- let mut suggestable_variants = variants
2526+ let mut suggestable_variants = variant_ctors
24852527 . iter ( )
24862528 . filter ( |( .., kind) | * kind == CtorKind :: Fn )
24872529 . map ( |( variant, ..) | path_names_to_string ( variant) )
24882530 . collect :: < Vec < _ > > ( ) ;
24892531 suggestable_variants. sort ( ) ;
24902532
2491- let non_suggestable_variant_count = variants . len ( ) - suggestable_variants. len ( ) ;
2533+ let non_suggestable_variant_count = variant_ctors . len ( ) - suggestable_variants. len ( ) ;
24922534
2493- let source_msg = if source. is_call ( ) {
2494- "to construct"
2495- } else if matches ! ( source, PathSource :: TupleStruct ( ..) ) {
2535+ let source_msg = if matches ! ( source, PathSource :: TupleStruct ( ..) ) {
24962536 "to match against"
24972537 } else {
2498- unreachable ! ( )
2538+ "to construct"
24992539 } ;
25002540
25012541 if !suggestable_variants. is_empty ( ) {
@@ -2514,7 +2554,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25142554 }
25152555
25162556 // If the enum has no tuple variants..
2517- if non_suggestable_variant_count == variants . len ( ) {
2557+ if non_suggestable_variant_count == variant_ctors . len ( ) {
25182558 err. help ( format ! ( "the enum has no tuple variants {source_msg}" ) ) ;
25192559 }
25202560
@@ -2537,7 +2577,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25372577 }
25382578 } ;
25392579
2540- let mut suggestable_variants = variants
2580+ let mut suggestable_variants = variant_ctors
25412581 . iter ( )
25422582 . filter ( |( _, def_id, kind) | !needs_placeholder ( * def_id, * kind) )
25432583 . map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
@@ -2564,7 +2604,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25642604 ) ;
25652605 }
25662606
2567- let mut suggestable_variants_with_placeholders = variants
2607+ let mut suggestable_variants_with_placeholders = variant_ctors
25682608 . iter ( )
25692609 . filter ( |( _, def_id, kind) | needs_placeholder ( * def_id, * kind) )
25702610 . map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
0 commit comments