@@ -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;
@@ -726,24 +726,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
726
726
// We already suggested changing `:` into `::` during parsing.
727
727
return false ;
728
728
}
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
- } ;
736
729
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) ;
747
731
}
748
732
( Res :: Def ( DefKind :: Struct , def_id) , _) if ns == ValueNS => {
749
733
if let Some ( ( ctor_def, ctor_vis, fields) ) =
@@ -1126,20 +1110,139 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
1126
1110
result
1127
1111
}
1128
1112
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 ) > > {
1130
1114
self . find_module ( def_id) . map ( |( enum_module, enum_import_suggestion) | {
1131
1115
let mut variants = Vec :: new ( ) ;
1132
1116
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 ( ) {
1134
1118
let mut segms = enum_import_suggestion. path . segments . clone ( ) ;
1135
1119
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) ) ;
1137
1122
}
1138
1123
} ) ;
1139
1124
variants
1140
1125
} )
1141
1126
}
1142
1127
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
+
1143
1246
crate fn report_missing_type_error (
1144
1247
& self ,
1145
1248
path : & [ Segment ] ,
0 commit comments