@@ -19,7 +19,7 @@ use syntax::util::parser::PREC_POSTFIX;
1919use syntax_pos:: Span ;
2020use rustc:: hir;
2121use rustc:: hir:: def:: Def ;
22- use rustc:: hir:: map:: NodeItem ;
22+ use rustc:: hir:: map:: { NodeItem , NodeExpr } ;
2323use rustc:: hir:: { Item , ItemConst , print} ;
2424use rustc:: ty:: { self , Ty , AssociatedItem } ;
2525use rustc:: ty:: adjustment:: AllowTwoPhase ;
@@ -140,8 +140,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
140140 }
141141 }
142142
143- if let Some ( ( msg, suggestion) ) = self . check_ref ( expr, checked_ty, expected) {
144- err. span_suggestion ( expr . span , msg, suggestion) ;
143+ if let Some ( ( sp , msg, suggestion) ) = self . check_ref ( expr, checked_ty, expected) {
144+ err. span_suggestion ( sp , msg, suggestion) ;
145145 } else if !self . check_for_cast ( & mut err, expr, expr_ty, expected) {
146146 let methods = self . get_conversion_methods ( expr. span , expected, checked_ty) ;
147147 if let Ok ( expr_text) = self . tcx . sess . codemap ( ) . span_to_snippet ( expr. span ) {
@@ -194,6 +194,57 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
194194 }
195195 }
196196
197+ /// Identify some cases where `as_ref()` would be appropriate and suggest it.
198+ ///
199+ /// Given the following code:
200+ /// ```
201+ /// struct Foo;
202+ /// fn takes_ref(_: &Foo) {}
203+ /// let ref opt = Some(Foo);
204+ ///
205+ /// opt.map(|arg| takes_ref(arg));
206+ /// ```
207+ /// Suggest using `opt.as_ref().map(|arg| takes_ref(arg));` instead.
208+ ///
209+ /// It only checks for `Option` and `Result` and won't work with
210+ /// ```
211+ /// opt.map(|arg| { takes_ref(arg) });
212+ /// ```
213+ fn can_use_as_ref ( & self , expr : & hir:: Expr ) -> Option < ( Span , & ' static str , String ) > {
214+ if let hir:: ExprPath ( hir:: QPath :: Resolved ( _, ref path) ) = expr. node {
215+ if let hir:: def:: Def :: Local ( id) = path. def {
216+ let parent = self . tcx . hir . get_parent_node ( id) ;
217+ if let Some ( NodeExpr ( hir:: Expr {
218+ id,
219+ node : hir:: ExprClosure ( _, decl, ..) ,
220+ ..
221+ } ) ) = self . tcx . hir . find ( parent) {
222+ let parent = self . tcx . hir . get_parent_node ( * id) ;
223+ if let ( Some ( NodeExpr ( hir:: Expr {
224+ node : hir:: ExprMethodCall ( path, span, expr) ,
225+ ..
226+ } ) ) , 1 ) = ( self . tcx . hir . find ( parent) , decl. inputs . len ( ) ) {
227+ let self_ty = self . tables . borrow ( ) . node_id_to_type ( expr[ 0 ] . hir_id ) ;
228+ let self_ty = format ! ( "{:?}" , self_ty) ;
229+ let name = path. name . as_str ( ) ;
230+ let is_as_ref_able = (
231+ self_ty. starts_with ( "&std::option::Option" ) ||
232+ self_ty. starts_with ( "&std::result::Result" ) ||
233+ self_ty. starts_with ( "std::option::Option" ) ||
234+ self_ty. starts_with ( "std::result::Result" )
235+ ) && ( name == "map" || name == "and_then" ) ;
236+ if is_as_ref_able {
237+ return Some ( ( span. shrink_to_lo ( ) ,
238+ "consider using `as_ref` instead" ,
239+ "as_ref()." . into ( ) ) ) ;
240+ }
241+ }
242+ }
243+ }
244+ }
245+ None
246+ }
247+
197248 /// This function is used to determine potential "simple" improvements or users' errors and
198249 /// provide them useful help. For example:
199250 ///
@@ -214,32 +265,33 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
214265 expr : & hir:: Expr ,
215266 checked_ty : Ty < ' tcx > ,
216267 expected : Ty < ' tcx > )
217- -> Option < ( & ' static str , String ) > {
268+ -> Option < ( Span , & ' static str , String ) > {
269+ let sp = expr. span ;
218270 match ( & expected. sty , & checked_ty. sty ) {
219271 ( & ty:: TyRef ( _, exp, _) , & ty:: TyRef ( _, check, _) ) => match ( & exp. sty , & check. sty ) {
220272 ( & ty:: TyStr , & ty:: TyArray ( arr, _) ) |
221273 ( & ty:: TyStr , & ty:: TySlice ( arr) ) if arr == self . tcx . types . u8 => {
222274 if let hir:: ExprLit ( _) = expr. node {
223275 let sp = self . sess ( ) . codemap ( ) . call_span_if_macro ( expr. span ) ;
224276 if let Ok ( src) = self . tcx . sess . codemap ( ) . span_to_snippet ( sp) {
225- return Some ( ( "consider removing the leading `b`" ,
277+ return Some ( ( sp,
278+ "consider removing the leading `b`" ,
226279 src[ 1 ..] . to_string ( ) ) ) ;
227280 }
228281 }
229- None
230282 } ,
231283 ( & ty:: TyArray ( arr, _) , & ty:: TyStr ) |
232284 ( & ty:: TySlice ( arr) , & ty:: TyStr ) if arr == self . tcx . types . u8 => {
233285 if let hir:: ExprLit ( _) = expr. node {
234286 let sp = self . sess ( ) . codemap ( ) . call_span_if_macro ( expr. span ) ;
235287 if let Ok ( src) = self . tcx . sess . codemap ( ) . span_to_snippet ( sp) {
236- return Some ( ( "consider adding a leading `b`" ,
288+ return Some ( ( sp,
289+ "consider adding a leading `b`" ,
237290 format ! ( "b{}" , src) ) ) ;
238291 }
239292 }
240- None
241293 }
242- _ => None ,
294+ _ => { }
243295 } ,
244296 ( & ty:: TyRef ( _, _, mutability) , _) => {
245297 // Check if it can work when put into a ref. For example:
@@ -266,17 +318,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
266318 hir:: ExprCast ( _, _) | hir:: ExprBinary ( _, _, _) => format ! ( "({})" , src) ,
267319 _ => src,
268320 } ;
321+ if let Some ( sugg) = self . can_use_as_ref ( expr) {
322+ return Some ( sugg) ;
323+ }
269324 return Some ( match mutability {
270325 hir:: Mutability :: MutMutable => {
271- ( "consider mutably borrowing here" , format ! ( "&mut {}" , sugg_expr) )
326+ ( sp, "consider mutably borrowing here" , format ! ( "&mut {}" ,
327+ sugg_expr) )
272328 }
273329 hir:: Mutability :: MutImmutable => {
274- ( "consider borrowing here" , format ! ( "&{}" , sugg_expr) )
330+ ( sp , "consider borrowing here" , format ! ( "&{}" , sugg_expr) )
275331 }
276332 } ) ;
277333 }
278334 }
279- None
280335 }
281336 ( _, & ty:: TyRef ( _, checked, _) ) => {
282337 // We have `&T`, check if what was expected was `T`. If so,
@@ -292,7 +347,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
292347 // Maybe remove `&`?
293348 hir:: ExprAddrOf ( _, ref expr) => {
294349 if let Ok ( code) = self . tcx . sess . codemap ( ) . span_to_snippet ( expr. span ) {
295- return Some ( ( "consider removing the borrow" , code) ) ;
350+ return Some ( ( sp , "consider removing the borrow" , code) ) ;
296351 }
297352 }
298353
@@ -303,17 +358,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
303358 expr. span ) {
304359 let sp = self . sess ( ) . codemap ( ) . call_span_if_macro ( expr. span ) ;
305360 if let Ok ( code) = self . tcx . sess . codemap ( ) . span_to_snippet ( sp) {
306- return Some ( ( "consider dereferencing the borrow" ,
361+ return Some ( ( sp,
362+ "consider dereferencing the borrow" ,
307363 format ! ( "*{}" , code) ) ) ;
308364 }
309365 }
310366 }
311367 }
312368 }
313- None
314369 }
315- _ => None ,
370+ _ => { }
316371 }
372+ None
317373 }
318374
319375 fn check_for_cast ( & self ,
0 commit comments