@@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxHashSet;
1212use rustc_errors:: { pluralize, struct_span_err, Applicability , DiagnosticBuilder } ;
1313use rustc_hir as hir;
1414use rustc_hir:: def:: Namespace :: { self , * } ;
15- use rustc_hir:: def:: { self , CtorKind , DefKind } ;
15+ use rustc_hir:: def:: { self , CtorKind , CtorOf , DefKind } ;
1616use rustc_hir:: def_id:: { DefId , CRATE_DEF_INDEX , LOCAL_CRATE } ;
1717use rustc_hir:: PrimTy ;
1818use rustc_session:: config:: nightly_options;
@@ -725,24 +725,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
725725 // We already suggested changing `:` into `::` during parsing.
726726 return false ;
727727 }
728- if let Some ( variants) = self . collect_enum_variants ( def_id) {
729- if !variants. is_empty ( ) {
730- let msg = if variants. len ( ) == 1 {
731- "try using the enum's variant"
732- } else {
733- "try using one of the enum's variants"
734- } ;
735728
736- err. span_suggestions (
737- span,
738- msg,
739- variants. iter ( ) . map ( path_names_to_string) ,
740- Applicability :: MaybeIncorrect ,
741- ) ;
742- }
743- } else {
744- err. note ( "you might have meant to use one of the enum's variants" ) ;
745- }
729+ self . suggest_using_enum_variant ( err, source, def_id, span) ;
746730 }
747731 ( Res :: Def ( DefKind :: Struct , def_id) , _) if ns == ValueNS => {
748732 if let Some ( ( ctor_def, ctor_vis, fields) ) =
@@ -1125,20 +1109,139 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
11251109 result
11261110 }
11271111
1128- fn collect_enum_variants ( & mut self , def_id : DefId ) -> Option < Vec < Path > > {
1112+ fn collect_enum_ctors ( & mut self , def_id : DefId ) -> Option < Vec < ( Path , DefId , CtorKind ) > > {
11291113 self . find_module ( def_id) . map ( |( enum_module, enum_import_suggestion) | {
11301114 let mut variants = Vec :: new ( ) ;
11311115 enum_module. for_each_child ( self . r , |_, ident, _, name_binding| {
1132- if let Res :: Def ( DefKind :: Variant , _ ) = name_binding. res ( ) {
1116+ if let Res :: Def ( DefKind :: Ctor ( CtorOf :: Variant , kind ) , def_id ) = name_binding. res ( ) {
11331117 let mut segms = enum_import_suggestion. path . segments . clone ( ) ;
11341118 segms. push ( ast:: PathSegment :: from_ident ( ident) ) ;
1135- variants. push ( Path { span : name_binding. span , segments : segms, tokens : None } ) ;
1119+ let path = Path { span : name_binding. span , segments : segms, tokens : None } ;
1120+ variants. push ( ( path, def_id, kind) ) ;
11361121 }
11371122 } ) ;
11381123 variants
11391124 } )
11401125 }
11411126
1127+ /// Adds a suggestion for using an enum's variant when an enum is used instead.
1128+ fn suggest_using_enum_variant (
1129+ & mut self ,
1130+ err : & mut DiagnosticBuilder < ' a > ,
1131+ source : PathSource < ' _ > ,
1132+ def_id : DefId ,
1133+ span : Span ,
1134+ ) {
1135+ let variants = match self . collect_enum_ctors ( def_id) {
1136+ Some ( variants) => variants,
1137+ None => {
1138+ err. note ( "you might have meant to use one of the enum's variants" ) ;
1139+ return ;
1140+ }
1141+ } ;
1142+
1143+ let suggest_only_tuple_variants =
1144+ matches ! ( source, PathSource :: TupleStruct ( ..) ) || source. is_call ( ) ;
1145+ let mut suggestable_variants = if suggest_only_tuple_variants {
1146+ // Suggest only tuple variants regardless of whether they have fields and do not
1147+ // suggest path with added parenthesis.
1148+ variants
1149+ . iter ( )
1150+ . filter ( |( .., kind) | * kind == CtorKind :: Fn )
1151+ . map ( |( variant, ..) | path_names_to_string ( variant) )
1152+ . collect :: < Vec < _ > > ( )
1153+ } else {
1154+ variants
1155+ . iter ( )
1156+ . filter ( |( _, def_id, kind) | {
1157+ // Suggest only variants that have no fields (these can definitely
1158+ // be constructed).
1159+ let has_fields =
1160+ self . r . field_names . get ( & def_id) . map ( |f| f. is_empty ( ) ) . unwrap_or ( false ) ;
1161+ match kind {
1162+ CtorKind :: Const => true ,
1163+ CtorKind :: Fn | CtorKind :: Fictive if has_fields => true ,
1164+ _ => false ,
1165+ }
1166+ } )
1167+ . map ( |( variant, _, kind) | ( path_names_to_string ( variant) , kind) )
1168+ . map ( |( variant_str, kind) | {
1169+ // Add constructor syntax where appropriate.
1170+ match kind {
1171+ CtorKind :: Const => variant_str,
1172+ CtorKind :: Fn => format ! ( "({}())" , variant_str) ,
1173+ CtorKind :: Fictive => format ! ( "({} {{}})" , variant_str) ,
1174+ }
1175+ } )
1176+ . collect :: < Vec < _ > > ( )
1177+ } ;
1178+
1179+ let non_suggestable_variant_count = variants. len ( ) - suggestable_variants. len ( ) ;
1180+
1181+ if !suggestable_variants. is_empty ( ) {
1182+ let msg = if non_suggestable_variant_count == 0 && suggestable_variants. len ( ) == 1 {
1183+ "try using the enum's variant"
1184+ } else {
1185+ "try using one of the enum's variants"
1186+ } ;
1187+
1188+ err. span_suggestions (
1189+ span,
1190+ msg,
1191+ suggestable_variants. drain ( ..) ,
1192+ Applicability :: MaybeIncorrect ,
1193+ ) ;
1194+ }
1195+
1196+ if suggest_only_tuple_variants {
1197+ let source_msg = if source. is_call ( ) {
1198+ "to construct"
1199+ } else if matches ! ( source, PathSource :: TupleStruct ( ..) ) {
1200+ "to match against"
1201+ } else {
1202+ unreachable ! ( )
1203+ } ;
1204+
1205+ // If the enum has no tuple variants..
1206+ if non_suggestable_variant_count == variants. len ( ) {
1207+ err. help ( & format ! ( "the enum has no tuple variants {}" , source_msg) ) ;
1208+ }
1209+
1210+ // If there are also non-tuple variants..
1211+ if non_suggestable_variant_count == 1 {
1212+ err. help ( & format ! (
1213+ "you might have meant {} the enum's non-tuple variant" ,
1214+ source_msg
1215+ ) ) ;
1216+ } else if non_suggestable_variant_count >= 1 {
1217+ err. help ( & format ! (
1218+ "you might have meant {} one of the enum's non-tuple variants" ,
1219+ source_msg
1220+ ) ) ;
1221+ }
1222+ } else {
1223+ let made_suggestion = non_suggestable_variant_count != variants. len ( ) ;
1224+ if made_suggestion {
1225+ if non_suggestable_variant_count == 1 {
1226+ err. help (
1227+ "you might have meant to use the enum's other variant that has fields" ,
1228+ ) ;
1229+ } else if non_suggestable_variant_count >= 1 {
1230+ err. help (
1231+ "you might have meant to use one of the enum's other variants that \
1232+ have fields",
1233+ ) ;
1234+ }
1235+ } else {
1236+ if non_suggestable_variant_count == 1 {
1237+ err. help ( "you might have meant to use the enum's variant" ) ;
1238+ } else if non_suggestable_variant_count >= 1 {
1239+ err. help ( "you might have meant to use one of the enum's variants" ) ;
1240+ }
1241+ }
1242+ }
1243+ }
1244+
11421245 crate fn report_missing_type_error (
11431246 & self ,
11441247 path : & [ Segment ] ,
0 commit comments