Explain why &T
is cloned when T
is not Clone
This commit is contained in:
parent
634770c0a7
commit
d12689c022
4 changed files with 87 additions and 7 deletions
|
@ -40,6 +40,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
|
||||
self.suggest_missing_parentheses(err, expr);
|
||||
self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
|
||||
self.note_type_is_not_clone(err, expected, expr_ty, expr);
|
||||
self.note_need_for_fn_pointer(err, expected, expr_ty);
|
||||
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
|
||||
self.report_closure_inferred_return_type(err, expected);
|
||||
|
@ -630,7 +631,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Applicability::MachineApplicable,
|
||||
true,
|
||||
));
|
||||
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
@ -2,8 +2,6 @@ use super::FnCtxt;
|
|||
use crate::astconv::AstConv;
|
||||
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_span::{self, Span};
|
||||
|
||||
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorOf, DefKind};
|
||||
|
@ -13,12 +11,14 @@ use rustc_hir::{
|
|||
WherePredicate,
|
||||
};
|
||||
use rustc_infer::infer::{self, TyCtxtInferExt};
|
||||
|
||||
use rustc_infer::traits;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Binder, Ty};
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_middle::ty::{self, Binder, ToPredicate, Ty};
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
|
||||
use std::iter;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
@ -846,4 +846,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let node = self.tcx.hir().get(id);
|
||||
matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
|
||||
}
|
||||
|
||||
/// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,
|
||||
/// which is a side-effect of autoref.
|
||||
pub(crate) fn note_type_is_not_clone(
|
||||
&self,
|
||||
diag: &mut Diagnostic,
|
||||
expected_ty: Ty<'tcx>,
|
||||
found_ty: Ty<'tcx>,
|
||||
expr: &hir::Expr<'_>,
|
||||
) {
|
||||
let hir::ExprKind::MethodCall(segment, &[ref callee_expr], _) = expr.kind else { return; };
|
||||
let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; };
|
||||
let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
|
||||
let results = self.typeck_results.borrow();
|
||||
// First, look for a `Clone::clone` call
|
||||
if segment.ident.name == sym::clone
|
||||
&& results.type_dependent_def_id(expr.hir_id).map_or(
|
||||
false,
|
||||
|did| {
|
||||
self.tcx.associated_item(did).container
|
||||
== ty::AssocItemContainer::TraitContainer(clone_trait_did)
|
||||
},
|
||||
)
|
||||
// If that clone call hasn't already dereferenced the self type (i.e. don't give this
|
||||
// diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
|
||||
&& !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
|
||||
// Check that we're in fact trying to clone into the expected type
|
||||
&& self.can_coerce(*pointee_ty, expected_ty)
|
||||
// And the expected type doesn't implement `Clone`
|
||||
&& !self.predicate_must_hold_considering_regions(&traits::Obligation {
|
||||
cause: traits::ObligationCause::dummy(),
|
||||
param_env: self.param_env,
|
||||
recursion_depth: 0,
|
||||
predicate: ty::Binder::dummy(ty::TraitRef {
|
||||
def_id: clone_trait_did,
|
||||
substs: self.tcx.mk_substs([expected_ty.into()].iter()),
|
||||
})
|
||||
.without_const()
|
||||
.to_predicate(self.tcx),
|
||||
})
|
||||
{
|
||||
diag.span_note(
|
||||
callee_expr.span,
|
||||
&format!(
|
||||
"`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
13
src/test/ui/typeck/explain_clone_autoref.rs
Normal file
13
src/test/ui/typeck/explain_clone_autoref.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
struct NotClone;
|
||||
|
||||
fn main() {
|
||||
clone_thing(&NotClone);
|
||||
}
|
||||
|
||||
fn clone_thing(nc: &NotClone) -> NotClone {
|
||||
//~^ NOTE expected `NotClone` because of return type
|
||||
nc.clone()
|
||||
//~^ ERROR mismatched type
|
||||
//~| NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
|
||||
//~| NOTE expected struct `NotClone`, found `&NotClone`
|
||||
}
|
18
src/test/ui/typeck/explain_clone_autoref.stderr
Normal file
18
src/test/ui/typeck/explain_clone_autoref.stderr
Normal file
|
@ -0,0 +1,18 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/explain_clone_autoref.rs:9:5
|
||||
|
|
||||
LL | fn clone_thing(nc: &NotClone) -> NotClone {
|
||||
| -------- expected `NotClone` because of return type
|
||||
LL |
|
||||
LL | nc.clone()
|
||||
| ^^^^^^^^^^ expected struct `NotClone`, found `&NotClone`
|
||||
|
|
||||
note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
|
||||
--> $DIR/explain_clone_autoref.rs:9:5
|
||||
|
|
||||
LL | nc.clone()
|
||||
| ^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
Loading…
Add table
Add a link
Reference in a new issue