@@ -23,11 +23,13 @@ use rustc::infer::type_variable::TypeVariableOrigin;
2323use rustc:: util:: nodemap:: FxHashSet ;
2424use rustc:: infer:: { self , InferOk } ;
2525use syntax:: ast;
26+ use syntax:: util:: lev_distance:: { lev_distance, find_best_match_for_name} ;
2627use syntax_pos:: Span ;
2728use rustc:: hir;
2829use std:: mem;
2930use std:: ops:: Deref ;
3031use std:: rc:: Rc ;
32+ use std:: cmp:: max;
3133
3234use self :: CandidateKind :: * ;
3335pub use self :: PickKind :: * ;
@@ -51,6 +53,10 @@ struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
5153 /// used for error reporting
5254 static_candidates : Vec < CandidateSource > ,
5355
56+ /// When probing for names, include names that are close to the
57+ /// requested name (by Levensthein distance)
58+ allow_similar_names : bool ,
59+
5460 /// Some(candidate) if there is a private candidate
5561 private_candidate : Option < Def > ,
5662
@@ -242,6 +248,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
242248 return Err ( MethodError :: NoMatch ( NoMatchData :: new ( Vec :: new ( ) ,
243249 Vec :: new ( ) ,
244250 Vec :: new ( ) ,
251+ None ,
245252 mode) ) )
246253 }
247254 }
@@ -261,7 +268,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
261268 // that we create during the probe process are removed later
262269 self . probe ( |_| {
263270 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) ) ;
265272
266273 probe_cx. assemble_inherent_candidates ( ) ;
267274 match scope {
@@ -333,7 +340,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
333340 mode : Mode ,
334341 method_name : Option < ast:: Name > ,
335342 return_type : Option < Ty < ' tcx > > ,
336- steps : Vec < CandidateStep < ' tcx > > )
343+ steps : Rc < Vec < CandidateStep < ' tcx > > > )
337344 -> ProbeContext < ' a , ' gcx , ' tcx > {
338345 ProbeContext {
339346 fcx,
@@ -344,8 +351,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
344351 inherent_candidates : Vec :: new ( ) ,
345352 extension_candidates : Vec :: new ( ) ,
346353 impl_dups : FxHashSet ( ) ,
347- steps : Rc :: new ( steps) ,
354+ steps : steps,
348355 static_candidates : Vec :: new ( ) ,
356+ allow_similar_names : false ,
349357 private_candidate : None ,
350358 unsatisfied_predicates : Vec :: new ( ) ,
351359 }
@@ -798,10 +806,12 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
798806 if let Some ( def) = private_candidate {
799807 return Err ( MethodError :: PrivateMatch ( def, out_of_scope_traits) ) ;
800808 }
809+ let lev_candidate = self . probe_for_lev_candidate ( ) ?;
801810
802811 Err ( MethodError :: NoMatch ( NoMatchData :: new ( static_candidates,
803812 unsatisfied_predicates,
804813 out_of_scope_traits,
814+ lev_candidate,
805815 self . mode ) ) )
806816 }
807817
@@ -913,11 +923,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
913923 debug ! ( "applicable_candidates: {:?}" , applicable_candidates) ;
914924
915925 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) ) ;
921928 }
922929 }
923930
@@ -1126,6 +1133,54 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
11261133 } )
11271134 }
11281135
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+
11291184 ///////////////////////////////////////////////////////////////////////////
11301185 // MISCELLANY
11311186 fn has_applicable_self ( & self , item : & ty:: AssociatedItem ) -> bool {
@@ -1253,10 +1308,21 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
12531308 self . tcx . erase_late_bound_regions ( value)
12541309 }
12551310
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.
12571313 fn impl_or_trait_item ( & self , def_id : DefId ) -> Vec < ty:: AssociatedItem > {
12581314 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+ }
12601326 } else {
12611327 self . tcx . associated_items ( def_id) . collect ( )
12621328 }
0 commit comments