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