use smallvec::{smallvec, SmallVec};
use std::ops::Deref;
-pub struct Coerce<'a, 'tcx> {
+struct Coerce<'a, 'tcx> {
fcx: &'a FnCtxt<'a, 'tcx>,
cause: ObligationCause<'tcx>,
use_lub: bool,
}
impl<'f, 'tcx> Coerce<'f, 'tcx> {
- pub fn new(
+ fn new(
fcx: &'f FnCtxt<'f, 'tcx>,
cause: ObligationCause<'tcx>,
allow_two_phase: AllowTwoPhase,
Coerce { fcx, cause, allow_two_phase, use_lub: false }
}
- pub fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
+ fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub);
self.commit_if_ok(|_| {
if self.use_lub {
self.probe(|_| coerce.coerce(source, target)).is_ok()
}
+ pub fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option<usize> {
+ let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable);
+ // We don't ever need two-phase here since we throw out the result of the coercion
+ let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
+ coerce
+ .autoderef(rustc_span::DUMMY_SP, expr_ty)
+ .find_map(|(ty, steps)| coerce.unify(ty, target).ok().map(|_| steps))
+ }
+
/// Given some expressions, their known unified type and another expression,
/// tries to unify the types, potentially inserting coercions on any of the
/// provided expressions and returns their LUB (aka "common supertype").
-use crate::check::coercion::Coerce;
use crate::check::FnCtxt;
use rustc_infer::infer::InferOk;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::{is_range_literal, Node};
-use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
use rustc_span::symbol::sym;
expr: &hir::Expr<'_>,
checked_ty: Ty<'tcx>,
expected: Ty<'tcx>,
- ) -> Option<(Span, &'static str, String)> {
+ ) -> Option<(Span, &'static str, String, Applicability)> {
let sm = self.sess().source_map();
let sp = expr.span;
if sm.is_imported(sp) {
// `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
let expr = expr.peel_drop_temps();
+ let remove_prefix = |s: String, prefix: &str| {
+ if s.starts_with(prefix) { Some(s[prefix.len()..].to_string()) } else { None }
+ };
+
match (&expr.kind, &expected.kind, &checked_ty.kind) {
(_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.kind, &check.kind) {
(&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
if let hir::ExprKind::Lit(_) = expr.kind {
if let Ok(src) = sm.span_to_snippet(sp) {
- if src.starts_with("b\"") {
+ if let Some(src) = remove_prefix(src, "b\"") {
return Some((
sp,
"consider removing the leading `b`",
- src[1..].to_string(),
+ format!("\"{}", src),
+ Applicability::MachineApplicable,
));
}
}
(&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
if let hir::ExprKind::Lit(_) = expr.kind {
if let Ok(src) = sm.span_to_snippet(sp) {
- if src.starts_with('"') {
+ if let Some(src) = remove_prefix(src, "\"") {
return Some((
sp,
"consider adding a leading `b`",
- format!("b{}", src),
+ format!("b\"{}", src),
+ Applicability::MachineApplicable,
));
}
}
let sugg_expr = if needs_parens { format!("({})", src) } else { src };
if let Some(sugg) = self.can_use_as_ref(expr) {
- return Some(sugg);
+ return Some((
+ sugg.0,
+ sugg.1,
+ sugg.2,
+ Applicability::MachineApplicable,
+ ));
}
let field_name = if is_struct_pat_shorthand_field {
format!("{}: ", sugg_expr)
"consider dereferencing here to assign to the mutable \
borrowed piece of memory",
format!("*{}", src),
+ Applicability::MachineApplicable,
));
}
}
sp,
"consider mutably borrowing here",
format!("{}&mut {}", field_name, sugg_expr),
+ Applicability::MachineApplicable,
),
hir::Mutability::Not => (
sp,
"consider borrowing here",
format!("{}&{}", field_name, sugg_expr),
+ Applicability::MachineApplicable,
),
});
}
// We have `&T`, check if what was expected was `T`. If so,
// we may want to suggest removing a `&`.
if sm.is_imported(expr.span) {
- if let Ok(code) = sm.span_to_snippet(sp) {
- if code.starts_with('&') {
+ if let Ok(src) = sm.span_to_snippet(sp) {
+ if let Some(src) = remove_prefix(src, "&") {
return Some((
sp,
"consider removing the borrow",
- code[1..].to_string(),
+ src,
+ Applicability::MachineApplicable,
));
}
}
return None;
}
if let Ok(code) = sm.span_to_snippet(expr.span) {
- return Some((sp, "consider removing the borrow", code));
+ return Some((
+ sp,
+ "consider removing the borrow",
+ code,
+ Applicability::MachineApplicable,
+ ));
}
}
(
_,
- &ty::RawPtr(TypeAndMut { ty: _, mutbl: hir::Mutability::Not }),
- &ty::Ref(_, _, hir::Mutability::Not),
+ &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }),
+ &ty::Ref(_, ty_a, mutbl_a),
) => {
- let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable);
- // We don't ever need two-phase here since we throw out the result of the coercion
- let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
-
- if let Some(steps) =
- coerce.autoderef(sp, checked_ty).skip(1).find_map(|(referent_ty, steps)| {
- coerce
- .unify(
- coerce.tcx.mk_ptr(ty::TypeAndMut {
- mutbl: hir::Mutability::Not,
- ty: referent_ty,
- }),
- expected,
- )
- .ok()
- .map(|_| steps)
- })
- {
- // The pointer type implements `Copy` trait so the suggestion is always valid.
- if let Ok(code) = sm.span_to_snippet(sp) {
- if code.starts_with('&') {
- let derefs = "*".repeat(steps - 1);
- let message = "consider dereferencing the reference";
- let suggestion = format!("&{}{}", derefs, code[1..].to_string());
- return Some((sp, message, suggestion));
+ if let Some(steps) = self.deref_steps(ty_a, ty_b) {
+ // Only suggest valid if dereferencing needed.
+ if steps > 0 {
+ // The pointer type implements `Copy` trait so the suggestion is always valid.
+ if let Ok(src) = sm.span_to_snippet(sp) {
+ let derefs = "*".repeat(steps);
+ match mutbl_b {
+ hir::Mutability::Mut => match mutbl_a {
+ hir::Mutability::Mut => {
+ if let Some(src) = remove_prefix(src, "&mut ") {
+ return Some((
+ sp,
+ "consider dereferencing",
+ format!("&mut {}{}", derefs, src),
+ Applicability::MachineApplicable,
+ ));
+ }
+ }
+ hir::Mutability::Not => {
+ if let Some(src) = remove_prefix(src, "&") {
+ return Some((
+ sp,
+ "consider dereferencing",
+ format!("&mut {}{}", derefs, src),
+ Applicability::Unspecified,
+ ));
+ }
+ }
+ },
+ hir::Mutability::Not => match mutbl_a {
+ hir::Mutability::Mut => {
+ if let Some(src) = remove_prefix(src, "&mut ") {
+ return Some((
+ sp,
+ "consider dereferencing",
+ format!("&{}{}", derefs, src),
+ Applicability::MachineApplicable,
+ ));
+ }
+ }
+ hir::Mutability::Not => {
+ if let Some(src) = remove_prefix(src, "&") {
+ return Some((
+ sp,
+ "consider dereferencing",
+ format!("&{}{}", derefs, src),
+ Applicability::MachineApplicable,
+ ));
+ }
+ }
+ },
+ }
}
}
}
} else {
format!("*{}", code)
};
- return Some((sp, message, suggestion));
+ return Some((sp, message, suggestion, Applicability::MachineApplicable));
}
}
}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-71676-1.rs:43:24
+ |
+LL | let _: *const u8 = &a;
+ | --------- ^^
+ | | |
+ | | expected `u8`, found struct `Emm`
+ | | help: consider dereferencing: `&***a`
+ | expected due to this
+ |
+ = note: expected raw pointer `*const u8`
+ found reference `&Emm`
+
+error[E0308]: mismatched types
+ --> $DIR/issue-71676-1.rs:46:22
+ |
+LL | let _: *mut u8 = &a;
+ | ------- ^^
+ | | |
+ | | types differ in mutability
+ | | help: consider dereferencing: `&mut ***a`
+ | expected due to this
+ |
+ = note: expected raw pointer `*mut u8`
+ found reference `&Emm`
+
+error[E0308]: mismatched types
+ --> $DIR/issue-71676-1.rs:49:24
+ |
+LL | let _: *const u8 = &mut a;
+ | --------- ^^^^^^
+ | | |
+ | | expected `u8`, found struct `Emm`
+ | | help: consider dereferencing: `&***a`
+ | expected due to this
+ |
+ = note: expected raw pointer `*const u8`
+ found mutable reference `&mut Emm`
+
+error[E0308]: mismatched types
+ --> $DIR/issue-71676-1.rs:52:22
+ |
+LL | let _: *mut u8 = &mut a;
+ | ------- ^^^^^^
+ | | |
+ | | expected `u8`, found struct `Emm`
+ | | help: consider dereferencing: `&mut ***a`
+ | expected due to this
+ |
+ = note: expected raw pointer `*mut u8`
+ found mutable reference `&mut Emm`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.