@@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxHashSet;
12
12
use rustc_errors:: { pluralize, struct_span_err, Applicability , DiagnosticBuilder } ;
13
13
use rustc_hir as hir;
14
14
use rustc_hir:: def:: Namespace :: { self , * } ;
15
- use rustc_hir:: def:: { self , CtorKind , DefKind } ;
15
+ use rustc_hir:: def:: { self , CtorKind , CtorOf , DefKind } ;
16
16
use rustc_hir:: def_id:: { DefId , CRATE_DEF_INDEX , LOCAL_CRATE } ;
17
17
use rustc_hir:: PrimTy ;
18
18
use rustc_session:: config:: nightly_options;
@@ -725,24 +725,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
725
725
// We already suggested changing `:` into `::` during parsing.
726
726
return false ;
727
727
}
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
- } ;
735
728
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) ;
746
730
}
747
731
( Res :: Def ( DefKind :: Struct , def_id) , _) if ns == ValueNS => {
748
732
if let Some ( ( ctor_def, ctor_vis, fields) ) =
@@ -1125,20 +1109,139 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
1125
1109
result
1126
1110
}
1127
1111
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 ) > > {
1129
1113
self . find_module ( def_id) . map ( |( enum_module, enum_import_suggestion) | {
1130
1114
let mut variants = Vec :: new ( ) ;
1131
1115
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 ( ) {
1133
1117
let mut segms = enum_import_suggestion. path . segments . clone ( ) ;
1134
1118
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) ) ;
1136
1121
}
1137
1122
} ) ;
1138
1123
variants
1139
1124
} )
1140
1125
}
1141
1126
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
+
1142
1245
crate fn report_missing_type_error (
1143
1246
& self ,
1144
1247
path : & [ Segment ] ,
0 commit comments