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_boxing_when_appropriate(err, expr, expected, expr_ty);
|
||||||
self.suggest_missing_parentheses(err, expr);
|
self.suggest_missing_parentheses(err, expr);
|
||||||
self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
|
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_need_for_fn_pointer(err, expected, expr_ty);
|
||||||
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
|
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
|
||||||
self.report_closure_inferred_return_type(err, expected);
|
self.report_closure_inferred_return_type(err, expected);
|
||||||
|
@ -630,7 +631,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
true,
|
true,
|
||||||
));
|
));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -2,8 +2,6 @@ use super::FnCtxt;
|
||||||
use crate::astconv::AstConv;
|
use crate::astconv::AstConv;
|
||||||
|
|
||||||
use rustc_ast::util::parser::ExprPrecedence;
|
use rustc_ast::util::parser::ExprPrecedence;
|
||||||
use rustc_span::{self, Span};
|
|
||||||
|
|
||||||
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
|
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{CtorOf, DefKind};
|
use rustc_hir::def::{CtorOf, DefKind};
|
||||||
|
@ -13,12 +11,14 @@ use rustc_hir::{
|
||||||
WherePredicate,
|
WherePredicate,
|
||||||
};
|
};
|
||||||
use rustc_infer::infer::{self, TyCtxtInferExt};
|
use rustc_infer::infer::{self, TyCtxtInferExt};
|
||||||
|
use rustc_infer::traits;
|
||||||
use rustc_middle::lint::in_external_macro;
|
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::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;
|
use std::iter;
|
||||||
|
|
||||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
@ -846,4 +846,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let node = self.tcx.hir().get(id);
|
let node = self.tcx.hir().get(id);
|
||||||
matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
|
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