Auto merge of #106607 - compiler-errors:be-more-accurate-abt-method-suggestions, r=oli-obk
Consider return type when giving various method suggestions 1. Fix a bug in method probe where we weren't normalizing `xform_ret_ty` for non-`impl` method candidates. This shouldn't affect happy-path code, since we only use `xform_ret_ty` when probing methods for diagnostics (I think). 2. Pass the return type expectation down to `lookup_probe`/`probe_for_name` usages in diagnostics. Added a few UI tests to gate against bad suggestions. 3. Make a `FnCtxt::lookup_probe_for_diagnostic` which properly passes down `IsSuggestion(true)`. Should help suppress other weird notes in some corner cases.
This commit is contained in:
commit
0442fbabe2
@ -1,4 +1,4 @@
|
||||
use super::method::probe::{IsSuggestion, Mode, ProbeScope};
|
||||
use super::method::probe::ProbeScope;
|
||||
use super::method::MethodCallee;
|
||||
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
|
||||
|
||||
@ -496,15 +496,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// any strange errors. If it's successful, then we'll do a true
|
||||
// method lookup.
|
||||
let Ok(pick) = self
|
||||
.probe_for_name(
|
||||
Mode::MethodCall,
|
||||
.lookup_probe_for_diagnostic(
|
||||
segment.ident,
|
||||
IsSuggestion(true),
|
||||
callee_ty,
|
||||
call_expr.hir_id,
|
||||
call_expr,
|
||||
// We didn't record the in scope traits during late resolution
|
||||
// so we need to probe AllTraits unfortunately
|
||||
ProbeScope::AllTraits,
|
||||
expected.only_has_type(self),
|
||||
) else {
|
||||
return None;
|
||||
};
|
||||
|
@ -303,11 +303,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// Get the evaluated type *after* calling the method call, so that the influence
|
||||
// of the arguments can be reflected in the receiver type. The receiver
|
||||
// expression has the type *before* theis analysis is done.
|
||||
let ty = match self.lookup_probe(
|
||||
let ty = match self.lookup_probe_for_diagnostic(
|
||||
segment.ident,
|
||||
rcvr_ty,
|
||||
expr,
|
||||
probe::ProbeScope::TraitsInScope,
|
||||
None,
|
||||
) {
|
||||
Ok(pick) => pick.self_ty,
|
||||
Err(_) => rcvr_ty,
|
||||
@ -557,19 +558,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let Some(self_ty) = self.typeck_results.borrow().expr_ty_adjusted_opt(base) else { return; };
|
||||
|
||||
let Ok(pick) = self
|
||||
.probe_for_name(
|
||||
probe::Mode::MethodCall,
|
||||
.lookup_probe_for_diagnostic(
|
||||
path.ident,
|
||||
probe::IsSuggestion(true),
|
||||
self_ty,
|
||||
deref.hir_id,
|
||||
deref,
|
||||
probe::ProbeScope::TraitsInScope,
|
||||
None,
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
let in_scope_methods = self.probe_for_name_many(
|
||||
probe::Mode::MethodCall,
|
||||
path.ident,
|
||||
Some(expected),
|
||||
probe::IsSuggestion(true),
|
||||
self_ty,
|
||||
deref.hir_id,
|
||||
@ -581,6 +582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let all_methods = self.probe_for_name_many(
|
||||
probe::Mode::MethodCall,
|
||||
path.ident,
|
||||
Some(expected),
|
||||
probe::IsSuggestion(true),
|
||||
self_ty,
|
||||
deref.hir_id,
|
||||
@ -1832,7 +1834,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub fn check_for_range_as_method_call(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expr: &hir::Expr<'_>,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
checked_ty: Ty<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
) {
|
||||
@ -1850,10 +1852,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
return;
|
||||
}
|
||||
let mut expr = end.expr;
|
||||
let mut expectation = Some(expected_ty);
|
||||
while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
|
||||
// Getting to the root receiver and asserting it is a fn call let's us ignore cases in
|
||||
// `src/test/ui/methods/issues/issue-90315.stderr`.
|
||||
expr = rcvr;
|
||||
// If we have more than one layer of calls, then the expected ty
|
||||
// cannot guide the method probe.
|
||||
expectation = None;
|
||||
}
|
||||
let hir::ExprKind::Call(method_name, _) = expr.kind else { return; };
|
||||
let ty::Adt(adt, _) = checked_ty.kind() else { return; };
|
||||
@ -1869,13 +1875,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else { return; };
|
||||
let [hir::PathSegment { ident, .. }] = p.segments else { return; };
|
||||
let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
|
||||
let Ok(_pick) = self.probe_for_name(
|
||||
probe::Mode::MethodCall,
|
||||
let Ok(_pick) = self.lookup_probe_for_diagnostic(
|
||||
*ident,
|
||||
probe::IsSuggestion(true),
|
||||
self_ty,
|
||||
expr.hir_id,
|
||||
expr,
|
||||
probe::ProbeScope::AllTraits,
|
||||
expectation,
|
||||
) else { return; };
|
||||
let mut sugg = ".";
|
||||
let mut span = start.expr.span.between(end.expr.span);
|
||||
|
@ -351,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
ExprKind::Struct(qpath, fields, ref base_expr) => {
|
||||
self.check_expr_struct(expr, expected, qpath, fields, base_expr)
|
||||
}
|
||||
ExprKind::Field(base, field) => self.check_field(expr, &base, field),
|
||||
ExprKind::Field(base, field) => self.check_field(expr, &base, field, expected),
|
||||
ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr),
|
||||
ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src),
|
||||
hir::ExprKind::Err => tcx.ty_error(),
|
||||
@ -1244,6 +1244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
SelfSource::MethodCall(rcvr),
|
||||
error,
|
||||
Some((rcvr, args)),
|
||||
expected,
|
||||
) {
|
||||
err.emit();
|
||||
}
|
||||
@ -2186,6 +2187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
base: &'tcx hir::Expr<'tcx>,
|
||||
field: Ident,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
|
||||
let base_ty = self.check_expr(base);
|
||||
@ -2244,12 +2246,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// (#90483) apply adjustments to avoid ExprUseVisitor from
|
||||
// creating erroneous projection.
|
||||
self.apply_adjustments(base, adjustments);
|
||||
self.ban_private_field_access(expr, base_ty, field, did);
|
||||
self.ban_private_field_access(expr, base_ty, field, did, expected.only_has_type(self));
|
||||
return self.tcx().ty_error();
|
||||
}
|
||||
|
||||
if field.name == kw::Empty {
|
||||
} else if self.method_exists(field, base_ty, expr.hir_id, true) {
|
||||
} else if self.method_exists(
|
||||
field,
|
||||
base_ty,
|
||||
expr.hir_id,
|
||||
true,
|
||||
expected.only_has_type(self),
|
||||
) {
|
||||
self.ban_take_value_of_method(expr, base_ty, field);
|
||||
} else if !base_ty.is_primitive_ty() {
|
||||
self.ban_nonexisting_field(field, base, expr, base_ty);
|
||||
@ -2423,10 +2431,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
fn ban_private_field_access(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
expr_t: Ty<'tcx>,
|
||||
field: Ident,
|
||||
base_did: DefId,
|
||||
return_ty: Option<Ty<'tcx>>,
|
||||
) {
|
||||
let struct_path = self.tcx().def_path_str(base_did);
|
||||
let kind_name = self.tcx().def_kind(base_did).descr(base_did);
|
||||
@ -2438,7 +2447,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
);
|
||||
err.span_label(field.span, "private field");
|
||||
// Also check if an accessible method exists, which is often what is meant.
|
||||
if self.method_exists(field, expr_t, expr.hir_id, false) && !self.expr_in_place(expr.hir_id)
|
||||
if self.method_exists(field, expr_t, expr.hir_id, false, return_ty)
|
||||
&& !self.expr_in_place(expr.hir_id)
|
||||
{
|
||||
self.suggest_method_call(
|
||||
&mut err,
|
||||
@ -2452,7 +2462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
err.emit();
|
||||
}
|
||||
|
||||
fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: Ident) {
|
||||
fn ban_take_value_of_method(&self, expr: &hir::Expr<'tcx>, expr_t: Ty<'tcx>, field: Ident) {
|
||||
let mut err = type_error_struct!(
|
||||
self.tcx().sess,
|
||||
field.span,
|
||||
|
@ -820,6 +820,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
SelfSource::QPath(qself),
|
||||
error,
|
||||
None,
|
||||
Expectation::NoExpectation,
|
||||
) {
|
||||
e.emit();
|
||||
}
|
||||
|
@ -1343,6 +1343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
if let Ok(pick) = self.probe_for_name(
|
||||
Mode::Path,
|
||||
Ident::new(capitalized_name, segment.ident.span),
|
||||
Some(expected_ty),
|
||||
IsSuggestion(true),
|
||||
self_ty,
|
||||
expr.hir_id,
|
||||
|
@ -97,10 +97,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self_ty: Ty<'tcx>,
|
||||
call_expr_id: hir::HirId,
|
||||
allow_private: bool,
|
||||
return_type: Option<Ty<'tcx>>,
|
||||
) -> bool {
|
||||
match self.probe_for_name(
|
||||
probe::Mode::MethodCall,
|
||||
method_name,
|
||||
return_type,
|
||||
IsSuggestion(false),
|
||||
self_ty,
|
||||
call_expr_id,
|
||||
@ -118,7 +120,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
Err(Ambiguity(..)) => true,
|
||||
Err(PrivateMatch(..)) => allow_private,
|
||||
Err(IllegalSizedBound { .. }) => true,
|
||||
Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"),
|
||||
Err(BadReturnType) => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,17 +132,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
msg: &str,
|
||||
method_name: Ident,
|
||||
self_ty: Ty<'tcx>,
|
||||
call_expr: &hir::Expr<'_>,
|
||||
call_expr: &hir::Expr<'tcx>,
|
||||
span: Option<Span>,
|
||||
) {
|
||||
let params = self
|
||||
.probe_for_name(
|
||||
probe::Mode::MethodCall,
|
||||
.lookup_probe_for_diagnostic(
|
||||
method_name,
|
||||
IsSuggestion(true),
|
||||
self_ty,
|
||||
call_expr.hir_id,
|
||||
call_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
None,
|
||||
)
|
||||
.map(|pick| {
|
||||
let sig = self.tcx.fn_sig(pick.item.def_id);
|
||||
@ -221,25 +222,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
// We probe again, taking all traits into account (not only those in scope).
|
||||
let candidates =
|
||||
match self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::AllTraits) {
|
||||
// If we find a different result the caller probably forgot to import a trait.
|
||||
Ok(ref new_pick) if pick.differs_from(new_pick) => {
|
||||
vec![new_pick.item.container_id(self.tcx)]
|
||||
}
|
||||
Err(Ambiguity(ref sources)) => sources
|
||||
.iter()
|
||||
.filter_map(|source| {
|
||||
match *source {
|
||||
// Note: this cannot come from an inherent impl,
|
||||
// because the first probing succeeded.
|
||||
CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
|
||||
CandidateSource::Trait(_) => None,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
let candidates = match self.lookup_probe_for_diagnostic(
|
||||
segment.ident,
|
||||
self_ty,
|
||||
call_expr,
|
||||
ProbeScope::AllTraits,
|
||||
None,
|
||||
) {
|
||||
// If we find a different result the caller probably forgot to import a trait.
|
||||
Ok(ref new_pick) if pick.differs_from(new_pick) => {
|
||||
vec![new_pick.item.container_id(self.tcx)]
|
||||
}
|
||||
Err(Ambiguity(ref sources)) => sources
|
||||
.iter()
|
||||
.filter_map(|source| {
|
||||
match *source {
|
||||
// Note: this cannot come from an inherent impl,
|
||||
// because the first probing succeeded.
|
||||
CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
|
||||
CandidateSource::Trait(_) => None,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
|
||||
return Err(IllegalSizedBound { candidates, needs_mut, bound_span: span, self_expr });
|
||||
}
|
||||
@ -252,12 +258,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self,
|
||||
method_name: Ident,
|
||||
self_ty: Ty<'tcx>,
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
call_expr: &hir::Expr<'_>,
|
||||
scope: ProbeScope,
|
||||
) -> probe::PickResult<'tcx> {
|
||||
let pick = self.probe_for_name(
|
||||
probe::Mode::MethodCall,
|
||||
method_name,
|
||||
None,
|
||||
IsSuggestion(false),
|
||||
self_ty,
|
||||
call_expr.hir_id,
|
||||
@ -267,6 +274,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
Ok(pick)
|
||||
}
|
||||
|
||||
pub fn lookup_probe_for_diagnostic(
|
||||
&self,
|
||||
method_name: Ident,
|
||||
self_ty: Ty<'tcx>,
|
||||
call_expr: &hir::Expr<'_>,
|
||||
scope: ProbeScope,
|
||||
return_type: Option<Ty<'tcx>>,
|
||||
) -> probe::PickResult<'tcx> {
|
||||
let pick = self.probe_for_name(
|
||||
probe::Mode::MethodCall,
|
||||
method_name,
|
||||
return_type,
|
||||
IsSuggestion(true),
|
||||
self_ty,
|
||||
call_expr.hir_id,
|
||||
scope,
|
||||
)?;
|
||||
Ok(pick)
|
||||
}
|
||||
|
||||
pub(super) fn obligation_for_method(
|
||||
&self,
|
||||
cause: ObligationCause<'tcx>,
|
||||
@ -484,6 +511,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let pick = self.probe_for_name(
|
||||
probe::Mode::Path,
|
||||
method_name,
|
||||
None,
|
||||
IsSuggestion(false),
|
||||
self_ty,
|
||||
expr_id,
|
||||
|
@ -304,6 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self,
|
||||
mode: Mode,
|
||||
item_name: Ident,
|
||||
return_type: Option<Ty<'tcx>>,
|
||||
is_suggestion: IsSuggestion,
|
||||
self_ty: Ty<'tcx>,
|
||||
scope_expr_id: hir::HirId,
|
||||
@ -313,7 +314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
item_name.span,
|
||||
mode,
|
||||
Some(item_name),
|
||||
None,
|
||||
return_type,
|
||||
is_suggestion,
|
||||
self_ty,
|
||||
scope_expr_id,
|
||||
@ -327,6 +328,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self,
|
||||
mode: Mode,
|
||||
item_name: Ident,
|
||||
return_type: Option<Ty<'tcx>>,
|
||||
is_suggestion: IsSuggestion,
|
||||
self_ty: Ty<'tcx>,
|
||||
scope_expr_id: hir::HirId,
|
||||
@ -336,7 +338,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
item_name.span,
|
||||
mode,
|
||||
Some(item_name),
|
||||
None,
|
||||
return_type,
|
||||
is_suggestion,
|
||||
self_ty,
|
||||
scope_expr_id,
|
||||
@ -1540,7 +1542,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||
let InferOk {
|
||||
value: normalized_xform_ret_ty,
|
||||
obligations: normalization_obligations,
|
||||
} = self.fcx.at(&cause, self.param_env).normalize(probe.xform_ret_ty);
|
||||
} = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty);
|
||||
xform_ret_ty = normalized_xform_ret_ty;
|
||||
debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty);
|
||||
|
||||
@ -1554,7 +1556,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||
|
||||
// Convert the bounds into obligations.
|
||||
let impl_obligations = traits::predicates_for_generics(
|
||||
move |_, _| cause.clone(),
|
||||
|_, _| cause.clone(),
|
||||
self.param_env,
|
||||
impl_bounds,
|
||||
);
|
||||
@ -1597,7 +1599,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||
ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx);
|
||||
parent_pred = Some(predicate);
|
||||
let obligation =
|
||||
traits::Obligation::new(self.tcx, cause, self.param_env, predicate);
|
||||
traits::Obligation::new(self.tcx, cause.clone(), self.param_env, predicate);
|
||||
if !self.predicate_may_hold(&obligation) {
|
||||
result = ProbeResult::NoMatch;
|
||||
if self.probe(|_| {
|
||||
@ -1656,22 +1658,48 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
if let ProbeResult::Match = result {
|
||||
if let (Some(return_ty), Some(xform_ret_ty)) = (self.return_type, xform_ret_ty) {
|
||||
let xform_ret_ty = self.resolve_vars_if_possible(xform_ret_ty);
|
||||
debug!(
|
||||
"comparing return_ty {:?} with xform ret ty {:?}",
|
||||
return_ty, probe.xform_ret_ty
|
||||
);
|
||||
if self
|
||||
.at(&ObligationCause::dummy(), self.param_env)
|
||||
.define_opaque_types(false)
|
||||
.sup(return_ty, xform_ret_ty)
|
||||
.is_err()
|
||||
{
|
||||
return ProbeResult::BadReturnType;
|
||||
if let ProbeResult::Match = result
|
||||
&& let Some(return_ty) = self.return_type
|
||||
&& let Some(mut xform_ret_ty) = xform_ret_ty
|
||||
{
|
||||
// `xform_ret_ty` has only been normalized for `InherentImplCandidate`.
|
||||
// We don't normalize the other candidates for perf/backwards-compat reasons...
|
||||
// but `self.return_type` is only set on the diagnostic-path, so we
|
||||
// should be okay doing it here.
|
||||
if !matches!(probe.kind, InherentImplCandidate(..)) {
|
||||
let InferOk {
|
||||
value: normalized_xform_ret_ty,
|
||||
obligations: normalization_obligations,
|
||||
} = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty);
|
||||
xform_ret_ty = normalized_xform_ret_ty;
|
||||
debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty);
|
||||
// Evaluate those obligations to see if they might possibly hold.
|
||||
for o in normalization_obligations {
|
||||
let o = self.resolve_vars_if_possible(o);
|
||||
if !self.predicate_may_hold(&o) {
|
||||
result = ProbeResult::NoMatch;
|
||||
possibly_unsatisfied_predicates.push((
|
||||
o.predicate,
|
||||
None,
|
||||
Some(o.cause),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
"comparing return_ty {:?} with xform ret ty {:?}",
|
||||
return_ty, xform_ret_ty
|
||||
);
|
||||
if let ProbeResult::Match = result
|
||||
&& self
|
||||
.at(&ObligationCause::dummy(), self.param_env)
|
||||
.define_opaque_types(false)
|
||||
.sup(return_ty, xform_ret_ty)
|
||||
.is_err()
|
||||
{
|
||||
result = ProbeResult::BadReturnType;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
|
@ -2,6 +2,7 @@
|
||||
//! found or is otherwise invalid.
|
||||
|
||||
use crate::errors;
|
||||
use crate::Expectation;
|
||||
use crate::FnCtxt;
|
||||
use rustc_ast::ast::Mutability;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
@ -108,6 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
source: SelfSource<'tcx>,
|
||||
error: MethodError<'tcx>,
|
||||
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
|
||||
// Avoid suggestions when we don't know what's going on.
|
||||
if rcvr_ty.references_error() {
|
||||
@ -131,6 +133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
args,
|
||||
sugg_span,
|
||||
&mut no_match_data,
|
||||
expected,
|
||||
);
|
||||
}
|
||||
|
||||
@ -250,6 +253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
|
||||
sugg_span: Span,
|
||||
no_match_data: &mut NoMatchData<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
|
||||
let mode = no_match_data.mode;
|
||||
let tcx = self.tcx;
|
||||
@ -320,7 +324,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
|
||||
self.suggest_await_before_method(
|
||||
&mut err, item_name, rcvr_ty, cal, span,
|
||||
&mut err, item_name, rcvr_ty, cal, span, expected.only_has_type(self),
|
||||
);
|
||||
}
|
||||
if let Some(span) =
|
||||
@ -366,8 +370,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| {
|
||||
let call_expr =
|
||||
self.tcx.hir().expect_expr(self.tcx.hir().parent_id(rcvr_expr.hir_id));
|
||||
let probe =
|
||||
self.lookup_probe(item_name, output_ty, call_expr, ProbeScope::AllTraits);
|
||||
let probe = self.lookup_probe_for_diagnostic(
|
||||
item_name,
|
||||
output_ty,
|
||||
call_expr,
|
||||
ProbeScope::AllTraits,
|
||||
expected.only_has_type(self),
|
||||
);
|
||||
probe.is_ok()
|
||||
});
|
||||
}
|
||||
@ -898,7 +907,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// Don't suggest (for example) `expr.field.clone()` if `expr.clone()`
|
||||
// can't be called due to `typeof(expr): Clone` not holding.
|
||||
if unsatisfied_predicates.is_empty() {
|
||||
self.suggest_calling_method_on_field(&mut err, source, span, rcvr_ty, item_name);
|
||||
self.suggest_calling_method_on_field(
|
||||
&mut err,
|
||||
source,
|
||||
span,
|
||||
rcvr_ty,
|
||||
item_name,
|
||||
expected.only_has_type(self),
|
||||
);
|
||||
}
|
||||
|
||||
self.check_for_inner_self(&mut err, source, rcvr_ty, item_name);
|
||||
@ -922,6 +938,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&unsatisfied_predicates,
|
||||
&static_candidates,
|
||||
unsatisfied_bounds,
|
||||
expected.only_has_type(self),
|
||||
);
|
||||
}
|
||||
|
||||
@ -987,7 +1004,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
self.check_for_deref_method(&mut err, source, rcvr_ty, item_name);
|
||||
self.check_for_deref_method(&mut err, source, rcvr_ty, item_name, expected);
|
||||
return Some(err);
|
||||
}
|
||||
|
||||
@ -1374,13 +1391,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let range_ty =
|
||||
self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]);
|
||||
|
||||
let pick = self.probe_for_name(
|
||||
Mode::MethodCall,
|
||||
let pick = self.lookup_probe_for_diagnostic(
|
||||
item_name,
|
||||
IsSuggestion(true),
|
||||
range_ty,
|
||||
expr.hir_id,
|
||||
expr,
|
||||
ProbeScope::AllTraits,
|
||||
None,
|
||||
);
|
||||
if pick.is_ok() {
|
||||
let range_span = parent_expr.span.with_hi(expr.span.hi());
|
||||
@ -1560,11 +1576,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&& let Some(expr) = visitor.result
|
||||
&& let Some(self_ty) = self.node_ty_opt(expr.hir_id)
|
||||
{
|
||||
let probe = self.lookup_probe(
|
||||
let probe = self.lookup_probe_for_diagnostic(
|
||||
seg2.ident,
|
||||
self_ty,
|
||||
call_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
None,
|
||||
);
|
||||
if probe.is_ok() {
|
||||
let sm = self.infcx.tcx.sess.source_map();
|
||||
@ -1587,6 +1604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
span: Span,
|
||||
actual: Ty<'tcx>,
|
||||
item_name: Ident,
|
||||
return_type: Option<Ty<'tcx>>,
|
||||
) {
|
||||
if let SelfSource::MethodCall(expr) = source
|
||||
&& let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
|
||||
@ -1610,11 +1628,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.check_for_nested_field_satisfying(
|
||||
span,
|
||||
&|_, field_ty| {
|
||||
self.lookup_probe(
|
||||
self.lookup_probe_for_diagnostic(
|
||||
item_name,
|
||||
field_ty,
|
||||
call_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
return_type,
|
||||
)
|
||||
.map_or(false, |pick| {
|
||||
!never_mention_traits
|
||||
@ -1680,9 +1699,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.lookup_probe(item_name, field_ty, call_expr, ProbeScope::TraitsInScope)
|
||||
.ok()
|
||||
.map(|pick| (variant, field, pick))
|
||||
self.lookup_probe_for_diagnostic(
|
||||
item_name,
|
||||
field_ty,
|
||||
call_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
None,
|
||||
)
|
||||
.ok()
|
||||
.map(|pick| (variant, field, pick))
|
||||
})
|
||||
.collect();
|
||||
|
||||
@ -1746,11 +1771,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
ty::AdtKind::Struct | ty::AdtKind::Union => {
|
||||
let [first] = ***substs else { return; };
|
||||
let ty::GenericArgKind::Type(ty) = first.unpack() else { return; };
|
||||
let Ok(pick) = self.lookup_probe(
|
||||
let Ok(pick) = self.lookup_probe_for_diagnostic(
|
||||
item_name,
|
||||
ty,
|
||||
call_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
None,
|
||||
) else { return; };
|
||||
|
||||
let name = self.ty_to_value_string(actual);
|
||||
@ -2010,12 +2036,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self_source: SelfSource<'tcx>,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
item_name: Ident,
|
||||
expected: Expectation<'tcx>,
|
||||
) {
|
||||
let SelfSource::QPath(ty) = self_source else { return; };
|
||||
for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) {
|
||||
if let Ok(pick) = self.probe_for_name(
|
||||
Mode::Path,
|
||||
item_name,
|
||||
expected.only_has_type(self),
|
||||
IsSuggestion(true),
|
||||
deref_ty,
|
||||
ty.hir_id,
|
||||
@ -2080,12 +2108,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
ty: Ty<'tcx>,
|
||||
call: &hir::Expr<'_>,
|
||||
span: Span,
|
||||
return_type: Option<Ty<'tcx>>,
|
||||
) {
|
||||
let output_ty = match self.get_impl_future_output_ty(ty) {
|
||||
Some(output_ty) => self.resolve_vars_if_possible(output_ty),
|
||||
_ => return,
|
||||
};
|
||||
let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true);
|
||||
let method_exists =
|
||||
self.method_exists(item_name, output_ty, call.hir_id, true, return_type);
|
||||
debug!("suggest_await_before_method: is_method_exist={}", method_exists);
|
||||
if method_exists {
|
||||
err.span_suggestion_verbose(
|
||||
@ -2199,6 +2229,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
)],
|
||||
static_candidates: &[CandidateSource],
|
||||
unsatisfied_bounds: bool,
|
||||
return_type: Option<Ty<'tcx>>,
|
||||
) {
|
||||
let mut alt_rcvr_sugg = false;
|
||||
if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) {
|
||||
@ -2221,7 +2252,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
(self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "),
|
||||
(self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"),
|
||||
] {
|
||||
match self.lookup_probe(item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) {
|
||||
match self.lookup_probe_for_diagnostic(
|
||||
item_name,
|
||||
*rcvr_ty,
|
||||
rcvr,
|
||||
ProbeScope::AllTraits,
|
||||
return_type,
|
||||
) {
|
||||
Ok(pick) => {
|
||||
// If the method is defined for the receiver we have, it likely wasn't `use`d.
|
||||
// We point at the method, but we just skip the rest of the check for arbitrary
|
||||
@ -2254,11 +2291,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
(self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"),
|
||||
] {
|
||||
if let Some(new_rcvr_t) = *rcvr_ty
|
||||
&& let Ok(pick) = self.lookup_probe(
|
||||
&& let Ok(pick) = self.lookup_probe_for_diagnostic(
|
||||
item_name,
|
||||
new_rcvr_t,
|
||||
rcvr,
|
||||
ProbeScope::AllTraits,
|
||||
return_type,
|
||||
)
|
||||
{
|
||||
debug!("try_alt_rcvr: pick candidate {:?}", pick);
|
||||
@ -2637,11 +2675,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
name: Symbol::intern(&format!("{}_else", method_name.as_str())),
|
||||
span: method_name.span,
|
||||
};
|
||||
let probe = self.lookup_probe(
|
||||
let probe = self.lookup_probe_for_diagnostic(
|
||||
new_name,
|
||||
self_ty,
|
||||
self_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
Some(expected),
|
||||
);
|
||||
|
||||
// check the method arguments number
|
||||
|
@ -0,0 +1,24 @@
|
||||
// edition:2021
|
||||
|
||||
// Test that we do not suggest `.await` when it doesn't make sense.
|
||||
|
||||
struct A;
|
||||
|
||||
impl A {
|
||||
fn test(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
async fn foo() -> A {
|
||||
A
|
||||
}
|
||||
|
||||
async fn async_main() {
|
||||
let x: u32 = foo().test();
|
||||
//~^ ERROR no method named `test` found for opaque type `impl Future<Output = A>` in the current scope
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = async_main();
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
error[E0599]: no method named `test` found for opaque type `impl Future<Output = A>` in the current scope
|
||||
--> $DIR/dont-suggest-await-on-method-return-mismatch.rs:18:24
|
||||
|
|
||||
LL | let x: u32 = foo().test();
|
||||
| ^^^^ method not found in `impl Future<Output = A>`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
@ -0,0 +1,18 @@
|
||||
struct Wrapper<T>(T);
|
||||
|
||||
impl Wrapper<Option<i32>> {
|
||||
fn inner_mut(&self) -> Option<&mut i32> {
|
||||
self.as_mut()
|
||||
//~^ ERROR no method named `as_mut` found for reference `&Wrapper<Option<i32>>` in the current scope
|
||||
//~| HELP one of the expressions' fields has a method of the same name
|
||||
//~| HELP items from traits can only be used if
|
||||
}
|
||||
|
||||
fn inner_mut_bad(&self) -> Option<&mut u32> {
|
||||
self.as_mut()
|
||||
//~^ ERROR no method named `as_mut` found for reference `&Wrapper<Option<i32>>` in the current scope
|
||||
//~| HELP items from traits can only be used if
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,27 @@
|
||||
error[E0599]: no method named `as_mut` found for reference `&Wrapper<Option<i32>>` in the current scope
|
||||
--> $DIR/field-method-suggestion-using-return-ty.rs:5:14
|
||||
|
|
||||
LL | self.as_mut()
|
||||
| ^^^^^^ method not found in `&Wrapper<Option<i32>>`
|
||||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `as_mut`, perhaps you need to implement it:
|
||||
candidate #1: `AsMut`
|
||||
help: one of the expressions' fields has a method of the same name
|
||||
|
|
||||
LL | self.0.as_mut()
|
||||
| ++
|
||||
|
||||
error[E0599]: no method named `as_mut` found for reference `&Wrapper<Option<i32>>` in the current scope
|
||||
--> $DIR/field-method-suggestion-using-return-ty.rs:12:14
|
||||
|
|
||||
LL | self.as_mut()
|
||||
| ^^^^^^ method not found in `&Wrapper<Option<i32>>`
|
||||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `as_mut`, perhaps you need to implement it:
|
||||
candidate #1: `AsMut`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
@ -3,11 +3,6 @@ error[E0616]: field `len` of struct `Foo` is private
|
||||
|
|
||||
LL | if x.len {
|
||||
| ^^^ private field
|
||||
|
|
||||
help: a method `len` also exists, call it with parentheses
|
||||
|
|
||||
LL | if x.len() {
|
||||
| ++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -0,0 +1,34 @@
|
||||
// run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
fn as_ref() -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
struct Type {
|
||||
option: Option<Vec<u8>>
|
||||
}
|
||||
trait Trait {
|
||||
fn foo(&self) -> &Vec<u8>;
|
||||
}
|
||||
impl Trait for Option<Vec<u8>> {
|
||||
fn foo(&self) -> &Vec<u8> {
|
||||
self.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
fn method(&self) -> Option<&Vec<u8>> {
|
||||
self.option.as_ref().map(|x| x)
|
||||
//~^ ERROR E0308
|
||||
}
|
||||
fn method2(&self) -> Option<&u8> {
|
||||
self.option.foo().get(0)
|
||||
//~^ ERROR E0425
|
||||
//~| ERROR E0308
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = Type { option: None }.method();
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
// run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
fn as_ref() -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
@ -5,20 +9,20 @@ struct Type {
|
||||
option: Option<Vec<u8>>
|
||||
}
|
||||
trait Trait {
|
||||
fn foo(&self) -> Vec<u8>;
|
||||
fn foo(&self) -> &Vec<u8>;
|
||||
}
|
||||
impl Trait for Option<Vec<u8>> {
|
||||
fn foo(&self) -> Vec<u8> {
|
||||
vec![1, 2, 3]
|
||||
fn foo(&self) -> &Vec<u8> {
|
||||
self.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
fn method(&self) -> Option<Vec<u8>> {
|
||||
fn method(&self) -> Option<&Vec<u8>> {
|
||||
self.option..as_ref().map(|x| x)
|
||||
//~^ ERROR E0308
|
||||
}
|
||||
fn method2(&self) -> &u8 {
|
||||
fn method2(&self) -> Option<&u8> {
|
||||
self.option..foo().get(0)
|
||||
//~^ ERROR E0425
|
||||
//~| ERROR E0308
|
||||
|
@ -1,5 +1,5 @@
|
||||
error[E0425]: cannot find function `foo` in this scope
|
||||
--> $DIR/method-access-to-range-literal-typo.rs:22:22
|
||||
--> $DIR/method-access-to-range-literal-typo.rs:26:22
|
||||
|
|
||||
LL | self.option..foo().get(0)
|
||||
| ^^^ not found in this scope
|
||||
@ -11,15 +11,15 @@ LL + self.option.foo().get(0)
|
||||
|
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/method-access-to-range-literal-typo.rs:18:9
|
||||
--> $DIR/method-access-to-range-literal-typo.rs:22:9
|
||||
|
|
||||
LL | fn method(&self) -> Option<Vec<u8>> {
|
||||
| --------------- expected `Option<Vec<u8>>` because of return type
|
||||
LL | fn method(&self) -> Option<&Vec<u8>> {
|
||||
| ---------------- expected `Option<&Vec<u8>>` because of return type
|
||||
LL | self.option..as_ref().map(|x| x)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found struct `Range`
|
||||
|
|
||||
= note: expected enum `Option<_>`
|
||||
found struct `std::ops::Range<Option<_>>`
|
||||
= note: expected enum `Option<&Vec<u8>>`
|
||||
found struct `std::ops::Range<Option<Vec<u8>>>`
|
||||
help: you likely meant to write a method call instead of a range
|
||||
|
|
||||
LL - self.option..as_ref().map(|x| x)
|
||||
@ -27,15 +27,15 @@ LL + self.option.as_ref().map(|x| x)
|
||||
|
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/method-access-to-range-literal-typo.rs:22:9
|
||||
--> $DIR/method-access-to-range-literal-typo.rs:26:9
|
||||
|
|
||||
LL | fn method2(&self) -> &u8 {
|
||||
| --- expected `&u8` because of return type
|
||||
LL | fn method2(&self) -> Option<&u8> {
|
||||
| ----------- expected `Option<&u8>` because of return type
|
||||
LL | self.option..foo().get(0)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found struct `Range`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found struct `Range`
|
||||
|
|
||||
= note: expected reference `&u8`
|
||||
found struct `std::ops::Range<Option<Vec<u8>>>`
|
||||
= note: expected enum `Option<&u8>`
|
||||
found struct `std::ops::Range<Option<Vec<u8>>>`
|
||||
help: you likely meant to write a method call instead of a range
|
||||
|
|
||||
LL - self.option..foo().get(0)
|
||||
|
Loading…
x
Reference in New Issue
Block a user