1
1
use crate::check::FnCtxt;
2
2
use rustc::infer::InferOk;
3
- use rustc::traits::{ObligationCause, ObligationCauseCode};
3
+ use rustc::traits::{self, ObligationCause, ObligationCauseCode};
4
4
5
5
use syntax::util::parser::PREC_POSTFIX;
6
6
use syntax_pos::Span;
@@ -324,8 +324,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
324
324
sp,
325
325
);
326
326
327
- match (&expected.sty, &checked_ty.sty) {
328
- (&ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.sty, &check.sty) {
327
+ // Check the `expn_info()` to see if this is a macro; if so, it's hard to
328
+ // extract the text and make a good suggestion, so don't bother.
329
+ let is_macro = sp.ctxt().outer().expn_info().is_some();
330
+
331
+ match (&expr.node, &expected.sty, &checked_ty.sty) {
332
+ (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.sty, &check.sty) {
329
333
(&ty::Str, &ty::Array(arr, _)) |
330
334
(&ty::Str, &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
331
335
if let hir::ExprKind::Lit(_) = expr.node {
@@ -352,7 +356,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
352
356
}
353
357
_ => {}
354
358
},
355
- (&ty::Ref(_, _, mutability), _) => {
359
+ (_, &ty::Ref(_, _, mutability), _) => {
356
360
// Check if it can work when put into a ref. For example:
357
361
//
358
362
// ```
@@ -407,60 +411,67 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
407
411
});
408
412
}
409
413
}
410
- }
411
- (_, &ty::Ref(_, checked, _)) => {
414
+ },
415
+ (hir::ExprKind::AddrOf(_, ref expr), _, &ty::Ref(_, checked, _)) if {
416
+ self.infcx.can_sub(self.param_env, checked, &expected).is_ok() && !is_macro
417
+ } => {
412
418
// We have `&T`, check if what was expected was `T`. If so,
413
- // we may want to suggest adding a `*`, or removing
414
- // a `&`.
415
- //
416
- // (But, also check the `expn_info()` to see if this is
417
- // a macro; if so, it's hard to extract the text and make a good
418
- // suggestion, so don't bother.)
419
- if self.infcx.can_sub(self.param_env, checked, &expected).is_ok() &&
420
- sp.ctxt().outer().expn_info().is_none() {
421
- match expr.node {
422
- // Maybe remove `&`?
423
- hir::ExprKind::AddrOf(_, ref expr) => {
424
- if !cm.span_to_filename(expr.span).is_real() {
425
- if let Ok(code) = cm.span_to_snippet(sp) {
426
- if code.chars().next() == Some('&') {
427
- return Some((
428
- sp,
429
- "consider removing the borrow",
430
- code[1..].to_string()),
431
- );
432
- }
433
- }
434
- return None;
435
- }
436
- if let Ok(code) = cm.span_to_snippet(expr.span) {
437
- return Some((sp, "consider removing the borrow", code));
438
- }
439
- }
440
-
441
- // Maybe add `*`? Only if `T: Copy`.
442
- _ => {
443
- if self.infcx.type_is_copy_modulo_regions(self.param_env,
444
- checked,
445
- sp) {
446
- // do not suggest if the span comes from a macro (#52783)
447
- if let (Ok(code), true) = (
448
- cm.span_to_snippet(sp),
449
- sp == expr.span,
450
- ) {
451
- return Some((
452
- sp,
453
- "consider dereferencing the borrow",
454
- if is_struct_pat_shorthand_field {
455
- format!("{}: *{}", code, code)
456
- } else {
457
- format!("*{}", code)
458
- },
459
- ));
460
- }
461
- }
419
+ // we may want to suggest removing a `&`.
420
+ if !cm.span_to_filename(expr.span).is_real() {
421
+ if let Ok(code) = cm.span_to_snippet(sp) {
422
+ if code.chars().next() == Some('&') {
423
+ return Some((
424
+ sp,
425
+ "consider removing the borrow",
426
+ code[1..].to_string(),
427
+ ));
462
428
}
463
429
}
430
+ return None;
431
+ }
432
+ if let Ok(code) = cm.span_to_snippet(expr.span) {
433
+ return Some((sp, "consider removing the borrow", code));
434
+ }
435
+ },
436
+ _ if sp == expr.span && !is_macro => {
437
+ // Check for `Deref` implementations by constructing a predicate to
438
+ // prove: `<T as Deref>::Output == U`
439
+ let deref_trait = self.tcx.lang_items().deref_trait().unwrap();
440
+ let item_def_id = self.tcx.associated_items(deref_trait).next().unwrap().def_id;
441
+ let predicate = ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate {
442
+ // `<T as Deref>::Output`
443
+ projection_ty: ty::ProjectionTy {
444
+ // `T`
445
+ substs: self.tcx.mk_substs_trait(
446
+ checked_ty,
447
+ self.fresh_substs_for_item(sp, item_def_id),
448
+ ),
449
+ // `Deref::Output`
450
+ item_def_id,
451
+ },
452
+ // `U`
453
+ ty: expected,
454
+ }));
455
+ let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
456
+ let impls_deref = self.infcx.predicate_may_hold(&obligation);
457
+
458
+ // For a suggestion to make sense, the type would need to be `Copy`.
459
+ let is_copy = self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp);
460
+
461
+ if is_copy && impls_deref {
462
+ if let Ok(code) = cm.span_to_snippet(sp) {
463
+ let message = if checked_ty.is_region_ptr() {
464
+ "consider dereferencing the borrow"
465
+ } else {
466
+ "consider dereferencing the type"
467
+ };
468
+ let suggestion = if is_struct_pat_shorthand_field {
469
+ format!("{}: *{}", code, code)
470
+ } else {
471
+ format!("*{}", code)
472
+ };
473
+ return Some((sp, message, suggestion));
474
+ }
464
475
}
465
476
}
466
477
_ => {}
0 commit comments