@@ -23,11 +23,13 @@ use rustc::infer::type_variable::TypeVariableOrigin;
23
23
use rustc:: util:: nodemap:: FxHashSet ;
24
24
use rustc:: infer:: { self , InferOk } ;
25
25
use syntax:: ast;
26
+ use syntax:: util:: lev_distance:: { lev_distance, find_best_match_for_name} ;
26
27
use syntax_pos:: Span ;
27
28
use rustc:: hir;
28
29
use std:: mem;
29
30
use std:: ops:: Deref ;
30
31
use std:: rc:: Rc ;
32
+ use std:: cmp:: max;
31
33
32
34
use self :: CandidateKind :: * ;
33
35
pub use self :: PickKind :: * ;
@@ -51,6 +53,10 @@ struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
51
53
/// used for error reporting
52
54
static_candidates : Vec < CandidateSource > ,
53
55
56
+ /// When probing for names, include names that are close to the
57
+ /// requested name (by Levensthein distance)
58
+ allow_similar_names : bool ,
59
+
54
60
/// Some(candidate) if there is a private candidate
55
61
private_candidate : Option < Def > ,
56
62
@@ -242,6 +248,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
242
248
return Err ( MethodError :: NoMatch ( NoMatchData :: new ( Vec :: new ( ) ,
243
249
Vec :: new ( ) ,
244
250
Vec :: new ( ) ,
251
+ None ,
245
252
mode) ) )
246
253
}
247
254
}
@@ -261,7 +268,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
261
268
// that we create during the probe process are removed later
262
269
self . probe ( |_| {
263
270
let mut probe_cx =
264
- ProbeContext :: new ( self , span, mode, method_name, return_type, steps) ;
271
+ ProbeContext :: new ( self , span, mode, method_name, return_type, Rc :: new ( steps) ) ;
265
272
266
273
probe_cx. assemble_inherent_candidates ( ) ;
267
274
match scope {
@@ -333,7 +340,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
333
340
mode : Mode ,
334
341
method_name : Option < ast:: Name > ,
335
342
return_type : Option < Ty < ' tcx > > ,
336
- steps : Vec < CandidateStep < ' tcx > > )
343
+ steps : Rc < Vec < CandidateStep < ' tcx > > > )
337
344
-> ProbeContext < ' a , ' gcx , ' tcx > {
338
345
ProbeContext {
339
346
fcx,
@@ -344,8 +351,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
344
351
inherent_candidates : Vec :: new ( ) ,
345
352
extension_candidates : Vec :: new ( ) ,
346
353
impl_dups : FxHashSet ( ) ,
347
- steps : Rc :: new ( steps) ,
354
+ steps : steps,
348
355
static_candidates : Vec :: new ( ) ,
356
+ allow_similar_names : false ,
349
357
private_candidate : None ,
350
358
unsatisfied_predicates : Vec :: new ( ) ,
351
359
}
@@ -798,10 +806,12 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
798
806
if let Some ( def) = private_candidate {
799
807
return Err ( MethodError :: PrivateMatch ( def, out_of_scope_traits) ) ;
800
808
}
809
+ let lev_candidate = self . probe_for_lev_candidate ( ) ?;
801
810
802
811
Err ( MethodError :: NoMatch ( NoMatchData :: new ( static_candidates,
803
812
unsatisfied_predicates,
804
813
out_of_scope_traits,
814
+ lev_candidate,
805
815
self . mode ) ) )
806
816
}
807
817
@@ -913,11 +923,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
913
923
debug ! ( "applicable_candidates: {:?}" , applicable_candidates) ;
914
924
915
925
if applicable_candidates. len ( ) > 1 {
916
- match self . collapse_candidates_to_trait_pick ( & applicable_candidates[ ..] ) {
917
- Some ( pick) => {
918
- return Some ( Ok ( pick) ) ;
919
- }
920
- None => { }
926
+ if let Some ( pick) = self . collapse_candidates_to_trait_pick ( & applicable_candidates[ ..] ) {
927
+ return Some ( Ok ( pick) ) ;
921
928
}
922
929
}
923
930
@@ -1126,6 +1133,54 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
1126
1133
} )
1127
1134
}
1128
1135
1136
+ /// Similarly to `probe_for_return_type`, this method attempts to find the best matching
1137
+ /// candidate method where the method name may have been misspelt. Similarly to other
1138
+ /// Levenshtein based suggestions, we provide at most one such suggestion.
1139
+ fn probe_for_lev_candidate ( & mut self ) -> Result < Option < ty:: AssociatedItem > , MethodError < ' tcx > > {
1140
+ debug ! ( "Probing for method names similar to {:?}" ,
1141
+ self . method_name) ;
1142
+
1143
+ let steps = self . steps . clone ( ) ;
1144
+ self . probe ( |_| {
1145
+ let mut pcx = ProbeContext :: new ( self . fcx , self . span , self . mode , self . method_name ,
1146
+ self . return_type , steps) ;
1147
+ pcx. allow_similar_names = true ;
1148
+ pcx. assemble_inherent_candidates ( ) ;
1149
+ pcx. assemble_extension_candidates_for_traits_in_scope ( ast:: DUMMY_NODE_ID ) ?;
1150
+
1151
+ let method_names = pcx. candidate_method_names ( ) ;
1152
+ pcx. allow_similar_names = false ;
1153
+ let applicable_close_candidates: Vec < ty:: AssociatedItem > = method_names
1154
+ . iter ( )
1155
+ . filter_map ( |& method_name| {
1156
+ pcx. reset ( ) ;
1157
+ pcx. method_name = Some ( method_name) ;
1158
+ pcx. assemble_inherent_candidates ( ) ;
1159
+ pcx. assemble_extension_candidates_for_traits_in_scope ( ast:: DUMMY_NODE_ID )
1160
+ . ok ( ) . map_or ( None , |_| {
1161
+ pcx. pick_core ( )
1162
+ . and_then ( |pick| pick. ok ( ) )
1163
+ . and_then ( |pick| Some ( pick. item ) )
1164
+ } )
1165
+ } )
1166
+ . collect ( ) ;
1167
+
1168
+ if applicable_close_candidates. is_empty ( ) {
1169
+ Ok ( None )
1170
+ } else {
1171
+ let best_name = {
1172
+ let names = applicable_close_candidates. iter ( ) . map ( |cand| & cand. name ) ;
1173
+ find_best_match_for_name ( names,
1174
+ & self . method_name . unwrap ( ) . as_str ( ) ,
1175
+ None )
1176
+ } . unwrap ( ) ;
1177
+ Ok ( applicable_close_candidates
1178
+ . into_iter ( )
1179
+ . find ( |method| method. name == best_name) )
1180
+ }
1181
+ } )
1182
+ }
1183
+
1129
1184
///////////////////////////////////////////////////////////////////////////
1130
1185
// MISCELLANY
1131
1186
fn has_applicable_self ( & self , item : & ty:: AssociatedItem ) -> bool {
@@ -1253,10 +1308,21 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
1253
1308
self . tcx . erase_late_bound_regions ( value)
1254
1309
}
1255
1310
1256
- /// Find the method with the appropriate name (or return type, as the case may be).
1311
+ /// Find the method with the appropriate name (or return type, as the case may be). If
1312
+ /// `allow_similar_names` is set, find methods with close-matching names.
1257
1313
fn impl_or_trait_item ( & self , def_id : DefId ) -> Vec < ty:: AssociatedItem > {
1258
1314
if let Some ( name) = self . method_name {
1259
- self . fcx . associated_item ( def_id, name) . map_or ( Vec :: new ( ) , |x| vec ! [ x] )
1315
+ if self . allow_similar_names {
1316
+ let max_dist = max ( name. as_str ( ) . len ( ) , 3 ) / 3 ;
1317
+ self . tcx . associated_items ( def_id)
1318
+ . filter ( |x| {
1319
+ let dist = lev_distance ( & * name. as_str ( ) , & x. name . as_str ( ) ) ;
1320
+ dist > 0 && dist <= max_dist
1321
+ } )
1322
+ . collect ( )
1323
+ } else {
1324
+ self . fcx . associated_item ( def_id, name) . map_or ( Vec :: new ( ) , |x| vec ! [ x] )
1325
+ }
1260
1326
} else {
1261
1327
self . tcx . associated_items ( def_id) . collect ( )
1262
1328
}
0 commit comments