@@ -19,7 +19,7 @@ use syntax::util::parser::PREC_POSTFIX;
19
19
use syntax_pos:: Span ;
20
20
use rustc:: hir;
21
21
use rustc:: hir:: def:: Def ;
22
- use rustc:: hir:: map:: NodeItem ;
22
+ use rustc:: hir:: map:: { NodeItem , NodeExpr } ;
23
23
use rustc:: hir:: { Item , ItemConst , print} ;
24
24
use rustc:: ty:: { self , Ty , AssociatedItem } ;
25
25
use rustc:: ty:: adjustment:: AllowTwoPhase ;
@@ -140,8 +140,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
140
140
}
141
141
}
142
142
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) ;
145
145
} else if !self . check_for_cast ( & mut err, expr, expr_ty, expected) {
146
146
let methods = self . get_conversion_methods ( expr. span , expected, checked_ty) ;
147
147
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> {
194
194
}
195
195
}
196
196
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
+
197
248
/// This function is used to determine potential "simple" improvements or users' errors and
198
249
/// provide them useful help. For example:
199
250
///
@@ -214,32 +265,33 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
214
265
expr : & hir:: Expr ,
215
266
checked_ty : Ty < ' tcx > ,
216
267
expected : Ty < ' tcx > )
217
- -> Option < ( & ' static str , String ) > {
268
+ -> Option < ( Span , & ' static str , String ) > {
269
+ let sp = expr. span ;
218
270
match ( & expected. sty , & checked_ty. sty ) {
219
271
( & ty:: TyRef ( _, exp, _) , & ty:: TyRef ( _, check, _) ) => match ( & exp. sty , & check. sty ) {
220
272
( & ty:: TyStr , & ty:: TyArray ( arr, _) ) |
221
273
( & ty:: TyStr , & ty:: TySlice ( arr) ) if arr == self . tcx . types . u8 => {
222
274
if let hir:: ExprLit ( _) = expr. node {
223
275
let sp = self . sess ( ) . codemap ( ) . call_span_if_macro ( expr. span ) ;
224
276
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`" ,
226
279
src[ 1 ..] . to_string ( ) ) ) ;
227
280
}
228
281
}
229
- None
230
282
} ,
231
283
( & ty:: TyArray ( arr, _) , & ty:: TyStr ) |
232
284
( & ty:: TySlice ( arr) , & ty:: TyStr ) if arr == self . tcx . types . u8 => {
233
285
if let hir:: ExprLit ( _) = expr. node {
234
286
let sp = self . sess ( ) . codemap ( ) . call_span_if_macro ( expr. span ) ;
235
287
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`" ,
237
290
format ! ( "b{}" , src) ) ) ;
238
291
}
239
292
}
240
- None
241
293
}
242
- _ => None ,
294
+ _ => { }
243
295
} ,
244
296
( & ty:: TyRef ( _, _, mutability) , _) => {
245
297
// Check if it can work when put into a ref. For example:
@@ -266,17 +318,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
266
318
hir:: ExprCast ( _, _) | hir:: ExprBinary ( _, _, _) => format ! ( "({})" , src) ,
267
319
_ => src,
268
320
} ;
321
+ if let Some ( sugg) = self . can_use_as_ref ( expr) {
322
+ return Some ( sugg) ;
323
+ }
269
324
return Some ( match mutability {
270
325
hir:: Mutability :: MutMutable => {
271
- ( "consider mutably borrowing here" , format ! ( "&mut {}" , sugg_expr) )
326
+ ( sp, "consider mutably borrowing here" , format ! ( "&mut {}" ,
327
+ sugg_expr) )
272
328
}
273
329
hir:: Mutability :: MutImmutable => {
274
- ( "consider borrowing here" , format ! ( "&{}" , sugg_expr) )
330
+ ( sp , "consider borrowing here" , format ! ( "&{}" , sugg_expr) )
275
331
}
276
332
} ) ;
277
333
}
278
334
}
279
- None
280
335
}
281
336
( _, & ty:: TyRef ( _, checked, _) ) => {
282
337
// We have `&T`, check if what was expected was `T`. If so,
@@ -292,7 +347,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
292
347
// Maybe remove `&`?
293
348
hir:: ExprAddrOf ( _, ref expr) => {
294
349
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) ) ;
296
351
}
297
352
}
298
353
@@ -303,17 +358,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
303
358
expr. span ) {
304
359
let sp = self . sess ( ) . codemap ( ) . call_span_if_macro ( expr. span ) ;
305
360
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" ,
307
363
format ! ( "*{}" , code) ) ) ;
308
364
}
309
365
}
310
366
}
311
367
}
312
368
}
313
- None
314
369
}
315
- _ => None ,
370
+ _ => { }
316
371
}
372
+ None
317
373
}
318
374
319
375
fn check_for_cast ( & self ,
0 commit comments