mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-22 03:44:24 +00:00
Hide unnecessary reference to trait
When the problem for a method not being found in its receiver is due to arbitrary self-types, we don't want to mention importing or implementing the trait, instead we suggest wrapping.
This commit is contained in:
parent
6f2d8a018e
commit
0195f8d375
@ -6,7 +6,7 @@ use crate::astconv::AstConv as _;
|
||||
use crate::check::cast;
|
||||
use crate::check::coercion::CoerceMany;
|
||||
use crate::check::fatally_break_rust;
|
||||
use crate::check::method::{probe, MethodError, SelfSource};
|
||||
use crate::check::method::SelfSource;
|
||||
use crate::check::report_unexpected_variant_res;
|
||||
use crate::check::BreakableCtxt;
|
||||
use crate::check::Diverges;
|
||||
@ -30,7 +30,6 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder,
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{ExprKind, QPath};
|
||||
use rustc_infer::infer;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
@ -461,7 +460,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id).1
|
||||
}
|
||||
|
||||
fn check_expr_path(&self, qpath: &hir::QPath<'_>, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> {
|
||||
fn check_expr_path(
|
||||
&self,
|
||||
qpath: &'tcx hir::QPath<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
let (res, opt_ty, segs) = self.resolve_ty_and_res_ufcs(qpath, expr.hir_id, expr.span);
|
||||
let ty = match res {
|
||||
@ -947,7 +950,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
Err(error) => {
|
||||
if segment.ident.name != kw::Empty {
|
||||
self.report_extended_method_error(segment, span, args, rcvr_t, error);
|
||||
if let Some(mut err) = self.report_method_error(
|
||||
span,
|
||||
rcvr_t,
|
||||
segment.ident,
|
||||
SelfSource::MethodCall(&args[0]),
|
||||
error,
|
||||
Some(args),
|
||||
) {
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
@ -964,82 +976,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
)
|
||||
}
|
||||
|
||||
fn report_extended_method_error(
|
||||
&self,
|
||||
segment: &hir::PathSegment<'_>,
|
||||
span: Span,
|
||||
args: &'tcx [hir::Expr<'tcx>],
|
||||
rcvr_t: Ty<'tcx>,
|
||||
error: MethodError<'tcx>,
|
||||
) {
|
||||
let rcvr = &args[0];
|
||||
let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, new_rcvr_t, pre: &str, post: &str| {
|
||||
if let Some(new_rcvr_t) = new_rcvr_t {
|
||||
if let Ok(pick) = self.lookup_probe(
|
||||
span,
|
||||
segment.ident,
|
||||
new_rcvr_t,
|
||||
rcvr,
|
||||
probe::ProbeScope::AllTraits,
|
||||
) {
|
||||
debug!("try_alt_rcvr: pick candidate {:?}", pick);
|
||||
// Make sure the method is defined for the *actual* receiver:
|
||||
// we don't want to treat `Box<Self>` as a receiver if
|
||||
// it only works because of an autoderef to `&self`
|
||||
if pick.autoderefs == 0
|
||||
// We don't want to suggest a container type when the missing method is
|
||||
// `.clone()`, otherwise we'd suggest `Arc::new(foo).clone()`, which is
|
||||
// far from what the user really wants.
|
||||
&& Some(pick.item.container.id()) != self.tcx.lang_items().clone_trait()
|
||||
{
|
||||
err.span_label(
|
||||
pick.item.ident.span,
|
||||
&format!("the method is available for `{}` here", new_rcvr_t),
|
||||
);
|
||||
err.multipart_suggestion(
|
||||
"consider wrapping the receiver expression with the appropriate type",
|
||||
vec![
|
||||
(rcvr.span.shrink_to_lo(), format!("{}({}", pre, post)),
|
||||
(rcvr.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(mut err) = self.report_method_error(
|
||||
span,
|
||||
rcvr_t,
|
||||
segment.ident,
|
||||
SelfSource::MethodCall(rcvr),
|
||||
error,
|
||||
Some(args),
|
||||
) {
|
||||
if let ty::Adt(..) = rcvr_t.kind() {
|
||||
// Try alternative arbitrary self types that could fulfill this call.
|
||||
// FIXME: probe for all types that *could* be arbitrary self-types, not
|
||||
// just this list.
|
||||
for (rcvr_t, post) in &[
|
||||
(rcvr_t, ""),
|
||||
(self.tcx.mk_mut_ref(&ty::ReErased, rcvr_t), "&mut "),
|
||||
(self.tcx.mk_imm_ref(&ty::ReErased, rcvr_t), "&"),
|
||||
] {
|
||||
for (rcvr_t, pre) in &[
|
||||
(self.tcx.mk_lang_item(rcvr_t, LangItem::OwnedBox), "Box::new"),
|
||||
(self.tcx.mk_lang_item(rcvr_t, LangItem::Pin), "Pin::new"),
|
||||
(self.tcx.mk_diagnostic_item(rcvr_t, sym::Arc), "Arc::new"),
|
||||
(self.tcx.mk_diagnostic_item(rcvr_t, sym::Rc), "Rc::new"),
|
||||
] {
|
||||
try_alt_rcvr(&mut err, *rcvr_t, pre, post);
|
||||
}
|
||||
}
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_cast(
|
||||
&self,
|
||||
e: &'tcx hir::Expr<'tcx>,
|
||||
|
@ -905,12 +905,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
/// Resolves an associated value path into a base type and associated constant, or method
|
||||
/// resolution. The newly resolved definition is written into `type_dependent_defs`.
|
||||
pub fn resolve_ty_and_res_ufcs<'b>(
|
||||
pub fn resolve_ty_and_res_ufcs(
|
||||
&self,
|
||||
qpath: &'b QPath<'b>,
|
||||
qpath: &'tcx QPath<'tcx>,
|
||||
hir_id: hir::HirId,
|
||||
span: Span,
|
||||
) -> (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]) {
|
||||
) -> (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
|
||||
debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span);
|
||||
let (ty, qself, item_segment) = match *qpath {
|
||||
QPath::Resolved(ref opt_qself, ref path) => {
|
||||
|
@ -68,12 +68,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_method_error<'b>(
|
||||
pub fn report_method_error(
|
||||
&self,
|
||||
span: Span,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
item_name: Ident,
|
||||
source: SelfSource<'b>,
|
||||
source: SelfSource<'tcx>,
|
||||
error: MethodError<'tcx>,
|
||||
args: Option<&'tcx [hir::Expr<'tcx>]>,
|
||||
) -> Option<DiagnosticBuilder<'_>> {
|
||||
@ -323,8 +323,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
err.span_suggestion(
|
||||
lit.span,
|
||||
&format!(
|
||||
"you must specify a concrete type for \
|
||||
this numeric value, like `{}`",
|
||||
"you must specify a concrete type for this numeric value, \
|
||||
like `{}`",
|
||||
concrete_type
|
||||
),
|
||||
format!("{}_{}", snippet, concrete_type),
|
||||
@ -975,17 +975,78 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_traits_to_import<'b>(
|
||||
fn suggest_traits_to_import(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
span: Span,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
item_name: Ident,
|
||||
source: SelfSource<'b>,
|
||||
source: SelfSource<'tcx>,
|
||||
valid_out_of_scope_traits: Vec<DefId>,
|
||||
unsatisfied_predicates: &[(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)],
|
||||
) {
|
||||
if self.suggest_valid_traits(err, valid_out_of_scope_traits) {
|
||||
let mut alt_rcvr_sugg = false;
|
||||
if let SelfSource::MethodCall(rcvr) = source {
|
||||
info!(?span, ?item_name, ?rcvr_ty, ?rcvr);
|
||||
if let ty::Adt(..) = rcvr_ty.kind() {
|
||||
// Try alternative arbitrary self types that could fulfill this call.
|
||||
// FIXME: probe for all types that *could* be arbitrary self-types, not
|
||||
// just this list.
|
||||
for (rcvr_ty, post) in &[
|
||||
(rcvr_ty, ""),
|
||||
(self.tcx.mk_mut_ref(&ty::ReErased, rcvr_ty), "&mut "),
|
||||
(self.tcx.mk_imm_ref(&ty::ReErased, rcvr_ty), "&"),
|
||||
] {
|
||||
for (rcvr_ty, pre) in &[
|
||||
(self.tcx.mk_lang_item(rcvr_ty, LangItem::OwnedBox), "Box::new"),
|
||||
(self.tcx.mk_lang_item(rcvr_ty, LangItem::Pin), "Pin::new"),
|
||||
(self.tcx.mk_diagnostic_item(rcvr_ty, sym::Arc), "Arc::new"),
|
||||
(self.tcx.mk_diagnostic_item(rcvr_ty, sym::Rc), "Rc::new"),
|
||||
] {
|
||||
if let Some(new_rcvr_t) = *rcvr_ty {
|
||||
if let Ok(pick) = self.lookup_probe(
|
||||
span,
|
||||
item_name,
|
||||
new_rcvr_t,
|
||||
rcvr,
|
||||
crate::check::method::probe::ProbeScope::AllTraits,
|
||||
) {
|
||||
debug!("try_alt_rcvr: pick candidate {:?}", pick);
|
||||
// Make sure the method is defined for the *actual* receiver:
|
||||
// we don't want to treat `Box<Self>` as a receiver if
|
||||
// it only works because of an autoderef to `&self`
|
||||
if pick.autoderefs == 0
|
||||
// We don't want to suggest a container type when the missing method is
|
||||
// `.clone()`, otherwise we'd suggest `Arc::new(foo).clone()`, which is
|
||||
// far from what the user really wants.
|
||||
&& Some(pick.item.container.id()) != self.tcx.lang_items().clone_trait()
|
||||
{
|
||||
err.span_label(
|
||||
pick.item.ident.span,
|
||||
&format!(
|
||||
"the method is available for `{}` here",
|
||||
new_rcvr_t
|
||||
),
|
||||
);
|
||||
err.multipart_suggestion(
|
||||
"consider wrapping the receiver expression with the \
|
||||
appropriate type",
|
||||
vec![
|
||||
(rcvr.span.shrink_to_lo(), format!("{}({}", pre, post)),
|
||||
(rcvr.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
// We don't care about the other suggestions.
|
||||
alt_rcvr_sugg = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !alt_rcvr_sugg && self.suggest_valid_traits(err, valid_out_of_scope_traits) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1075,6 +1136,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
"the method might not be found because of this arbitrary self type",
|
||||
);
|
||||
}
|
||||
if alt_rcvr_sugg {
|
||||
return;
|
||||
}
|
||||
|
||||
if !candidates.is_empty() {
|
||||
// Sort from most relevant to least relevant.
|
||||
@ -1284,7 +1348,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
/// Checks whether there is a local type somewhere in the chain of
|
||||
/// autoderefs of `rcvr_ty`.
|
||||
fn type_derefs_to_local(&self, span: Span, rcvr_ty: Ty<'tcx>, source: SelfSource<'_>) -> bool {
|
||||
fn type_derefs_to_local(
|
||||
&self,
|
||||
span: Span,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
source: SelfSource<'tcx>,
|
||||
) -> bool {
|
||||
fn is_local(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Adt(def, _) => def.did.is_local(),
|
||||
@ -1310,7 +1379,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum SelfSource<'a> {
|
||||
QPath(&'a hir::Ty<'a>),
|
||||
MethodCall(&'a hir::Expr<'a> /* rcvr */),
|
||||
|
@ -861,7 +861,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
fn check_pat_tuple_struct(
|
||||
&self,
|
||||
pat: &'tcx Pat<'tcx>,
|
||||
qpath: &hir::QPath<'_>,
|
||||
qpath: &'tcx hir::QPath<'tcx>,
|
||||
subpats: &'tcx [&'tcx Pat<'tcx>],
|
||||
ddpos: Option<usize>,
|
||||
expected: Ty<'tcx>,
|
||||
|
@ -12,9 +12,6 @@ LL | self.sleep.poll(cx)
|
||||
LL | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
|
||||
| ---- the method is available for `Pin<&mut Sleep>` here
|
||||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `poll`, perhaps you need to implement it:
|
||||
candidate #1: `Future`
|
||||
help: consider wrapping the receiver expression with the appropriate type
|
||||
|
|
||||
LL | Pin::new(&mut self.sleep).poll(cx)
|
||||
|
Loading…
Reference in New Issue
Block a user