Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0442fba

Browse files
committedJan 10, 2023
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.
2 parents deba5dd + 4f15034 commit 0442fba

16 files changed

+329
-107
lines changed
 

‎compiler/rustc_hir_typeck/src/callee.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::method::probe::{IsSuggestion, Mode, ProbeScope};
1+
use super::method::probe::ProbeScope;
22
use super::method::MethodCallee;
33
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
44

@@ -496,15 +496,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
496496
// any strange errors. If it's successful, then we'll do a true
497497
// method lookup.
498498
let Ok(pick) = self
499-
.probe_for_name(
500-
Mode::MethodCall,
499+
.lookup_probe_for_diagnostic(
501500
segment.ident,
502-
IsSuggestion(true),
503501
callee_ty,
504-
call_expr.hir_id,
502+
call_expr,
505503
// We didn't record the in scope traits during late resolution
506504
// so we need to probe AllTraits unfortunately
507505
ProbeScope::AllTraits,
506+
expected.only_has_type(self),
508507
) else {
509508
return None;
510509
};

‎compiler/rustc_hir_typeck/src/demand.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -303,11 +303,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
303303
// Get the evaluated type *after* calling the method call, so that the influence
304304
// of the arguments can be reflected in the receiver type. The receiver
305305
// expression has the type *before* theis analysis is done.
306-
let ty = match self.lookup_probe(
306+
let ty = match self.lookup_probe_for_diagnostic(
307307
segment.ident,
308308
rcvr_ty,
309309
expr,
310310
probe::ProbeScope::TraitsInScope,
311+
None,
311312
) {
312313
Ok(pick) => pick.self_ty,
313314
Err(_) => rcvr_ty,
@@ -557,19 +558,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
557558
let Some(self_ty) = self.typeck_results.borrow().expr_ty_adjusted_opt(base) else { return; };
558559

559560
let Ok(pick) = self
560-
.probe_for_name(
561-
probe::Mode::MethodCall,
561+
.lookup_probe_for_diagnostic(
562562
path.ident,
563-
probe::IsSuggestion(true),
564563
self_ty,
565-
deref.hir_id,
564+
deref,
566565
probe::ProbeScope::TraitsInScope,
566+
None,
567567
) else {
568568
return;
569569
};
570570
let in_scope_methods = self.probe_for_name_many(
571571
probe::Mode::MethodCall,
572572
path.ident,
573+
Some(expected),
573574
probe::IsSuggestion(true),
574575
self_ty,
575576
deref.hir_id,
@@ -581,6 +582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
581582
let all_methods = self.probe_for_name_many(
582583
probe::Mode::MethodCall,
583584
path.ident,
585+
Some(expected),
584586
probe::IsSuggestion(true),
585587
self_ty,
586588
deref.hir_id,
@@ -1832,7 +1834,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18321834
pub fn check_for_range_as_method_call(
18331835
&self,
18341836
err: &mut Diagnostic,
1835-
expr: &hir::Expr<'_>,
1837+
expr: &hir::Expr<'tcx>,
18361838
checked_ty: Ty<'tcx>,
18371839
expected_ty: Ty<'tcx>,
18381840
) {
@@ -1850,10 +1852,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18501852
return;
18511853
}
18521854
let mut expr = end.expr;
1855+
let mut expectation = Some(expected_ty);
18531856
while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
18541857
// Getting to the root receiver and asserting it is a fn call let's us ignore cases in
18551858
// `src/test/ui/methods/issues/issue-90315.stderr`.
18561859
expr = rcvr;
1860+
// If we have more than one layer of calls, then the expected ty
1861+
// cannot guide the method probe.
1862+
expectation = None;
18571863
}
18581864
let hir::ExprKind::Call(method_name, _) = expr.kind else { return; };
18591865
let ty::Adt(adt, _) = checked_ty.kind() else { return; };
@@ -1869,13 +1875,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18691875
let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else { return; };
18701876
let [hir::PathSegment { ident, .. }] = p.segments else { return; };
18711877
let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
1872-
let Ok(_pick) = self.probe_for_name(
1873-
probe::Mode::MethodCall,
1878+
let Ok(_pick) = self.lookup_probe_for_diagnostic(
18741879
*ident,
1875-
probe::IsSuggestion(true),
18761880
self_ty,
1877-
expr.hir_id,
1881+
expr,
18781882
probe::ProbeScope::AllTraits,
1883+
expectation,
18791884
) else { return; };
18801885
let mut sugg = ".";
18811886
let mut span = start.expr.span.between(end.expr.span);

‎compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
351351
ExprKind::Struct(qpath, fields, ref base_expr) => {
352352
self.check_expr_struct(expr, expected, qpath, fields, base_expr)
353353
}
354-
ExprKind::Field(base, field) => self.check_field(expr, &base, field),
354+
ExprKind::Field(base, field) => self.check_field(expr, &base, field, expected),
355355
ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr),
356356
ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src),
357357
hir::ExprKind::Err => tcx.ty_error(),
@@ -1244,6 +1244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12441244
SelfSource::MethodCall(rcvr),
12451245
error,
12461246
Some((rcvr, args)),
1247+
expected,
12471248
) {
12481249
err.emit();
12491250
}
@@ -2186,6 +2187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21862187
expr: &'tcx hir::Expr<'tcx>,
21872188
base: &'tcx hir::Expr<'tcx>,
21882189
field: Ident,
2190+
expected: Expectation<'tcx>,
21892191
) -> Ty<'tcx> {
21902192
debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
21912193
let base_ty = self.check_expr(base);
@@ -2244,12 +2246,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22442246
// (#90483) apply adjustments to avoid ExprUseVisitor from
22452247
// creating erroneous projection.
22462248
self.apply_adjustments(base, adjustments);
2247-
self.ban_private_field_access(expr, base_ty, field, did);
2249+
self.ban_private_field_access(expr, base_ty, field, did, expected.only_has_type(self));
22482250
return self.tcx().ty_error();
22492251
}
22502252

22512253
if field.name == kw::Empty {
2252-
} else if self.method_exists(field, base_ty, expr.hir_id, true) {
2254+
} else if self.method_exists(
2255+
field,
2256+
base_ty,
2257+
expr.hir_id,
2258+
true,
2259+
expected.only_has_type(self),
2260+
) {
22532261
self.ban_take_value_of_method(expr, base_ty, field);
22542262
} else if !base_ty.is_primitive_ty() {
22552263
self.ban_nonexisting_field(field, base, expr, base_ty);
@@ -2423,10 +2431,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24232431

24242432
fn ban_private_field_access(
24252433
&self,
2426-
expr: &hir::Expr<'_>,
2434+
expr: &hir::Expr<'tcx>,
24272435
expr_t: Ty<'tcx>,
24282436
field: Ident,
24292437
base_did: DefId,
2438+
return_ty: Option<Ty<'tcx>>,
24302439
) {
24312440
let struct_path = self.tcx().def_path_str(base_did);
24322441
let kind_name = self.tcx().def_kind(base_did).descr(base_did);
@@ -2438,7 +2447,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24382447
);
24392448
err.span_label(field.span, "private field");
24402449
// Also check if an accessible method exists, which is often what is meant.
2441-
if self.method_exists(field, expr_t, expr.hir_id, false) && !self.expr_in_place(expr.hir_id)
2450+
if self.method_exists(field, expr_t, expr.hir_id, false, return_ty)
2451+
&& !self.expr_in_place(expr.hir_id)
24422452
{
24432453
self.suggest_method_call(
24442454
&mut err,
@@ -2452,7 +2462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24522462
err.emit();
24532463
}
24542464

2455-
fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: Ident) {
2465+
fn ban_take_value_of_method(&self, expr: &hir::Expr<'tcx>, expr_t: Ty<'tcx>, field: Ident) {
24562466
let mut err = type_error_struct!(
24572467
self.tcx().sess,
24582468
field.span,

‎compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
820820
SelfSource::QPath(qself),
821821
error,
822822
None,
823+
Expectation::NoExpectation,
823824
) {
824825
e.emit();
825826
}

‎compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13431343
if let Ok(pick) = self.probe_for_name(
13441344
Mode::Path,
13451345
Ident::new(capitalized_name, segment.ident.span),
1346+
Some(expected_ty),
13461347
IsSuggestion(true),
13471348
self_ty,
13481349
expr.hir_id,

‎compiler/rustc_hir_typeck/src/method/mod.rs

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9797
self_ty: Ty<'tcx>,
9898
call_expr_id: hir::HirId,
9999
allow_private: bool,
100+
return_type: Option<Ty<'tcx>>,
100101
) -> bool {
101102
match self.probe_for_name(
102103
probe::Mode::MethodCall,
103104
method_name,
105+
return_type,
104106
IsSuggestion(false),
105107
self_ty,
106108
call_expr_id,
@@ -118,7 +120,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
118120
Err(Ambiguity(..)) => true,
119121
Err(PrivateMatch(..)) => allow_private,
120122
Err(IllegalSizedBound { .. }) => true,
121-
Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"),
123+
Err(BadReturnType) => false,
122124
}
123125
}
124126

@@ -130,17 +132,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
130132
msg: &str,
131133
method_name: Ident,
132134
self_ty: Ty<'tcx>,
133-
call_expr: &hir::Expr<'_>,
135+
call_expr: &hir::Expr<'tcx>,
134136
span: Option<Span>,
135137
) {
136138
let params = self
137-
.probe_for_name(
138-
probe::Mode::MethodCall,
139+
.lookup_probe_for_diagnostic(
139140
method_name,
140-
IsSuggestion(true),
141141
self_ty,
142-
call_expr.hir_id,
142+
call_expr,
143143
ProbeScope::TraitsInScope,
144+
None,
144145
)
145146
.map(|pick| {
146147
let sig = self.tcx.fn_sig(pick.item.def_id);
@@ -221,25 +222,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
221222
}
222223

223224
// We probe again, taking all traits into account (not only those in scope).
224-
let candidates =
225-
match self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::AllTraits) {
226-
// If we find a different result the caller probably forgot to import a trait.
227-
Ok(ref new_pick) if pick.differs_from(new_pick) => {
228-
vec![new_pick.item.container_id(self.tcx)]
229-
}
230-
Err(Ambiguity(ref sources)) => sources
231-
.iter()
232-
.filter_map(|source| {
233-
match *source {
234-
// Note: this cannot come from an inherent impl,
235-
// because the first probing succeeded.
236-
CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
237-
CandidateSource::Trait(_) => None,
238-
}
239-
})
240-
.collect(),
241-
_ => Vec::new(),
242-
};
225+
let candidates = match self.lookup_probe_for_diagnostic(
226+
segment.ident,
227+
self_ty,
228+
call_expr,
229+
ProbeScope::AllTraits,
230+
None,
231+
) {
232+
// If we find a different result the caller probably forgot to import a trait.
233+
Ok(ref new_pick) if pick.differs_from(new_pick) => {
234+
vec![new_pick.item.container_id(self.tcx)]
235+
}
236+
Err(Ambiguity(ref sources)) => sources
237+
.iter()
238+
.filter_map(|source| {
239+
match *source {
240+
// Note: this cannot come from an inherent impl,
241+
// because the first probing succeeded.
242+
CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
243+
CandidateSource::Trait(_) => None,
244+
}
245+
})
246+
.collect(),
247+
_ => Vec::new(),
248+
};
243249

244250
return Err(IllegalSizedBound { candidates, needs_mut, bound_span: span, self_expr });
245251
}
@@ -252,12 +258,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
252258
&self,
253259
method_name: Ident,
254260
self_ty: Ty<'tcx>,
255-
call_expr: &'tcx hir::Expr<'tcx>,
261+
call_expr: &hir::Expr<'_>,
256262
scope: ProbeScope,
257263
) -> probe::PickResult<'tcx> {
258264
let pick = self.probe_for_name(
259265
probe::Mode::MethodCall,
260266
method_name,
267+
None,
261268
IsSuggestion(false),
262269
self_ty,
263270
call_expr.hir_id,
@@ -267,6 +274,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
267274
Ok(pick)
268275
}
269276

277+
pub fn lookup_probe_for_diagnostic(
278+
&self,
279+
method_name: Ident,
280+
self_ty: Ty<'tcx>,
281+
call_expr: &hir::Expr<'_>,
282+
scope: ProbeScope,
283+
return_type: Option<Ty<'tcx>>,
284+
) -> probe::PickResult<'tcx> {
285+
let pick = self.probe_for_name(
286+
probe::Mode::MethodCall,
287+
method_name,
288+
return_type,
289+
IsSuggestion(true),
290+
self_ty,
291+
call_expr.hir_id,
292+
scope,
293+
)?;
294+
Ok(pick)
295+
}
296+
270297
pub(super) fn obligation_for_method(
271298
&self,
272299
cause: ObligationCause<'tcx>,
@@ -484,6 +511,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
484511
let pick = self.probe_for_name(
485512
probe::Mode::Path,
486513
method_name,
514+
None,
487515
IsSuggestion(false),
488516
self_ty,
489517
expr_id,

‎compiler/rustc_hir_typeck/src/method/probe.rs

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
304304
&self,
305305
mode: Mode,
306306
item_name: Ident,
307+
return_type: Option<Ty<'tcx>>,
307308
is_suggestion: IsSuggestion,
308309
self_ty: Ty<'tcx>,
309310
scope_expr_id: hir::HirId,
@@ -313,7 +314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
313314
item_name.span,
314315
mode,
315316
Some(item_name),
316-
None,
317+
return_type,
317318
is_suggestion,
318319
self_ty,
319320
scope_expr_id,
@@ -327,6 +328,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
327328
&self,
328329
mode: Mode,
329330
item_name: Ident,
331+
return_type: Option<Ty<'tcx>>,
330332
is_suggestion: IsSuggestion,
331333
self_ty: Ty<'tcx>,
332334
scope_expr_id: hir::HirId,
@@ -336,7 +338,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
336338
item_name.span,
337339
mode,
338340
Some(item_name),
339-
None,
341+
return_type,
340342
is_suggestion,
341343
self_ty,
342344
scope_expr_id,
@@ -1540,7 +1542,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
15401542
let InferOk {
15411543
value: normalized_xform_ret_ty,
15421544
obligations: normalization_obligations,
1543-
} = self.fcx.at(&cause, self.param_env).normalize(probe.xform_ret_ty);
1545+
} = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty);
15441546
xform_ret_ty = normalized_xform_ret_ty;
15451547
debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty);
15461548

@@ -1554,7 +1556,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
15541556

15551557
// Convert the bounds into obligations.
15561558
let impl_obligations = traits::predicates_for_generics(
1557-
move |_, _| cause.clone(),
1559+
|_, _| cause.clone(),
15581560
self.param_env,
15591561
impl_bounds,
15601562
);
@@ -1597,7 +1599,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
15971599
ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx);
15981600
parent_pred = Some(predicate);
15991601
let obligation =
1600-
traits::Obligation::new(self.tcx, cause, self.param_env, predicate);
1602+
traits::Obligation::new(self.tcx, cause.clone(), self.param_env, predicate);
16011603
if !self.predicate_may_hold(&obligation) {
16021604
result = ProbeResult::NoMatch;
16031605
if self.probe(|_| {
@@ -1656,22 +1658,48 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
16561658
}
16571659
}
16581660

1659-
if let ProbeResult::Match = result {
1660-
if let (Some(return_ty), Some(xform_ret_ty)) = (self.return_type, xform_ret_ty) {
1661-
let xform_ret_ty = self.resolve_vars_if_possible(xform_ret_ty);
1662-
debug!(
1663-
"comparing return_ty {:?} with xform ret ty {:?}",
1664-
return_ty, probe.xform_ret_ty
1665-
);
1666-
if self
1667-
.at(&ObligationCause::dummy(), self.param_env)
1668-
.define_opaque_types(false)
1669-
.sup(return_ty, xform_ret_ty)
1670-
.is_err()
1671-
{
1672-
return ProbeResult::BadReturnType;
1661+
if let ProbeResult::Match = result
1662+
&& let Some(return_ty) = self.return_type
1663+
&& let Some(mut xform_ret_ty) = xform_ret_ty
1664+
{
1665+
// `xform_ret_ty` has only been normalized for `InherentImplCandidate`.
1666+
// We don't normalize the other candidates for perf/backwards-compat reasons...
1667+
// but `self.return_type` is only set on the diagnostic-path, so we
1668+
// should be okay doing it here.
1669+
if !matches!(probe.kind, InherentImplCandidate(..)) {
1670+
let InferOk {
1671+
value: normalized_xform_ret_ty,
1672+
obligations: normalization_obligations,
1673+
} = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty);
1674+
xform_ret_ty = normalized_xform_ret_ty;
1675+
debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty);
1676+
// Evaluate those obligations to see if they might possibly hold.
1677+
for o in normalization_obligations {
1678+
let o = self.resolve_vars_if_possible(o);
1679+
if !self.predicate_may_hold(&o) {
1680+
result = ProbeResult::NoMatch;
1681+
possibly_unsatisfied_predicates.push((
1682+
o.predicate,
1683+
None,
1684+
Some(o.cause),
1685+
));
1686+
}
16731687
}
16741688
}
1689+
1690+
debug!(
1691+
"comparing return_ty {:?} with xform ret ty {:?}",
1692+
return_ty, xform_ret_ty
1693+
);
1694+
if let ProbeResult::Match = result
1695+
&& self
1696+
.at(&ObligationCause::dummy(), self.param_env)
1697+
.define_opaque_types(false)
1698+
.sup(return_ty, xform_ret_ty)
1699+
.is_err()
1700+
{
1701+
result = ProbeResult::BadReturnType;
1702+
}
16751703
}
16761704

16771705
result

‎compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! found or is otherwise invalid.
33
44
use crate::errors;
5+
use crate::Expectation;
56
use crate::FnCtxt;
67
use rustc_ast::ast::Mutability;
78
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -108,6 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
108109
source: SelfSource<'tcx>,
109110
error: MethodError<'tcx>,
110111
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
112+
expected: Expectation<'tcx>,
111113
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
112114
// Avoid suggestions when we don't know what's going on.
113115
if rcvr_ty.references_error() {
@@ -131,6 +133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
131133
args,
132134
sugg_span,
133135
&mut no_match_data,
136+
expected,
134137
);
135138
}
136139

@@ -250,6 +253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
250253
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
251254
sugg_span: Span,
252255
no_match_data: &mut NoMatchData<'tcx>,
256+
expected: Expectation<'tcx>,
253257
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
254258
let mode = no_match_data.mode;
255259
let tcx = self.tcx;
@@ -320,7 +324,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
320324

321325
if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
322326
self.suggest_await_before_method(
323-
&mut err, item_name, rcvr_ty, cal, span,
327+
&mut err, item_name, rcvr_ty, cal, span, expected.only_has_type(self),
324328
);
325329
}
326330
if let Some(span) =
@@ -366,8 +370,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
366370
self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| {
367371
let call_expr =
368372
self.tcx.hir().expect_expr(self.tcx.hir().parent_id(rcvr_expr.hir_id));
369-
let probe =
370-
self.lookup_probe(item_name, output_ty, call_expr, ProbeScope::AllTraits);
373+
let probe = self.lookup_probe_for_diagnostic(
374+
item_name,
375+
output_ty,
376+
call_expr,
377+
ProbeScope::AllTraits,
378+
expected.only_has_type(self),
379+
);
371380
probe.is_ok()
372381
});
373382
}
@@ -898,7 +907,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
898907
// Don't suggest (for example) `expr.field.clone()` if `expr.clone()`
899908
// can't be called due to `typeof(expr): Clone` not holding.
900909
if unsatisfied_predicates.is_empty() {
901-
self.suggest_calling_method_on_field(&mut err, source, span, rcvr_ty, item_name);
910+
self.suggest_calling_method_on_field(
911+
&mut err,
912+
source,
913+
span,
914+
rcvr_ty,
915+
item_name,
916+
expected.only_has_type(self),
917+
);
902918
}
903919

904920
self.check_for_inner_self(&mut err, source, rcvr_ty, item_name);
@@ -922,6 +938,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
922938
&unsatisfied_predicates,
923939
&static_candidates,
924940
unsatisfied_bounds,
941+
expected.only_has_type(self),
925942
);
926943
}
927944

@@ -987,7 +1004,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9871004
}
9881005
}
9891006

990-
self.check_for_deref_method(&mut err, source, rcvr_ty, item_name);
1007+
self.check_for_deref_method(&mut err, source, rcvr_ty, item_name, expected);
9911008
return Some(err);
9921009
}
9931010

@@ -1374,13 +1391,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13741391
let range_ty =
13751392
self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]);
13761393

1377-
let pick = self.probe_for_name(
1378-
Mode::MethodCall,
1394+
let pick = self.lookup_probe_for_diagnostic(
13791395
item_name,
1380-
IsSuggestion(true),
13811396
range_ty,
1382-
expr.hir_id,
1397+
expr,
13831398
ProbeScope::AllTraits,
1399+
None,
13841400
);
13851401
if pick.is_ok() {
13861402
let range_span = parent_expr.span.with_hi(expr.span.hi());
@@ -1560,11 +1576,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15601576
&& let Some(expr) = visitor.result
15611577
&& let Some(self_ty) = self.node_ty_opt(expr.hir_id)
15621578
{
1563-
let probe = self.lookup_probe(
1579+
let probe = self.lookup_probe_for_diagnostic(
15641580
seg2.ident,
15651581
self_ty,
15661582
call_expr,
15671583
ProbeScope::TraitsInScope,
1584+
None,
15681585
);
15691586
if probe.is_ok() {
15701587
let sm = self.infcx.tcx.sess.source_map();
@@ -1587,6 +1604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15871604
span: Span,
15881605
actual: Ty<'tcx>,
15891606
item_name: Ident,
1607+
return_type: Option<Ty<'tcx>>,
15901608
) {
15911609
if let SelfSource::MethodCall(expr) = source
15921610
&& let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
@@ -1610,11 +1628,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16101628
self.check_for_nested_field_satisfying(
16111629
span,
16121630
&|_, field_ty| {
1613-
self.lookup_probe(
1631+
self.lookup_probe_for_diagnostic(
16141632
item_name,
16151633
field_ty,
16161634
call_expr,
16171635
ProbeScope::TraitsInScope,
1636+
return_type,
16181637
)
16191638
.map_or(false, |pick| {
16201639
!never_mention_traits
@@ -1680,9 +1699,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16801699
return None;
16811700
}
16821701

1683-
self.lookup_probe(item_name, field_ty, call_expr, ProbeScope::TraitsInScope)
1684-
.ok()
1685-
.map(|pick| (variant, field, pick))
1702+
self.lookup_probe_for_diagnostic(
1703+
item_name,
1704+
field_ty,
1705+
call_expr,
1706+
ProbeScope::TraitsInScope,
1707+
None,
1708+
)
1709+
.ok()
1710+
.map(|pick| (variant, field, pick))
16861711
})
16871712
.collect();
16881713

@@ -1746,11 +1771,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17461771
ty::AdtKind::Struct | ty::AdtKind::Union => {
17471772
let [first] = ***substs else { return; };
17481773
let ty::GenericArgKind::Type(ty) = first.unpack() else { return; };
1749-
let Ok(pick) = self.lookup_probe(
1774+
let Ok(pick) = self.lookup_probe_for_diagnostic(
17501775
item_name,
17511776
ty,
17521777
call_expr,
17531778
ProbeScope::TraitsInScope,
1779+
None,
17541780
) else { return; };
17551781

17561782
let name = self.ty_to_value_string(actual);
@@ -2010,12 +2036,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20102036
self_source: SelfSource<'tcx>,
20112037
rcvr_ty: Ty<'tcx>,
20122038
item_name: Ident,
2039+
expected: Expectation<'tcx>,
20132040
) {
20142041
let SelfSource::QPath(ty) = self_source else { return; };
20152042
for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) {
20162043
if let Ok(pick) = self.probe_for_name(
20172044
Mode::Path,
20182045
item_name,
2046+
expected.only_has_type(self),
20192047
IsSuggestion(true),
20202048
deref_ty,
20212049
ty.hir_id,
@@ -2080,12 +2108,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20802108
ty: Ty<'tcx>,
20812109
call: &hir::Expr<'_>,
20822110
span: Span,
2111+
return_type: Option<Ty<'tcx>>,
20832112
) {
20842113
let output_ty = match self.get_impl_future_output_ty(ty) {
20852114
Some(output_ty) => self.resolve_vars_if_possible(output_ty),
20862115
_ => return,
20872116
};
2088-
let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true);
2117+
let method_exists =
2118+
self.method_exists(item_name, output_ty, call.hir_id, true, return_type);
20892119
debug!("suggest_await_before_method: is_method_exist={}", method_exists);
20902120
if method_exists {
20912121
err.span_suggestion_verbose(
@@ -2199,6 +2229,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21992229
)],
22002230
static_candidates: &[CandidateSource],
22012231
unsatisfied_bounds: bool,
2232+
return_type: Option<Ty<'tcx>>,
22022233
) {
22032234
let mut alt_rcvr_sugg = false;
22042235
if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) {
@@ -2221,7 +2252,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22212252
(self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "),
22222253
(self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"),
22232254
] {
2224-
match self.lookup_probe(item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) {
2255+
match self.lookup_probe_for_diagnostic(
2256+
item_name,
2257+
*rcvr_ty,
2258+
rcvr,
2259+
ProbeScope::AllTraits,
2260+
return_type,
2261+
) {
22252262
Ok(pick) => {
22262263
// If the method is defined for the receiver we have, it likely wasn't `use`d.
22272264
// 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> {
22542291
(self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"),
22552292
] {
22562293
if let Some(new_rcvr_t) = *rcvr_ty
2257-
&& let Ok(pick) = self.lookup_probe(
2294+
&& let Ok(pick) = self.lookup_probe_for_diagnostic(
22582295
item_name,
22592296
new_rcvr_t,
22602297
rcvr,
22612298
ProbeScope::AllTraits,
2299+
return_type,
22622300
)
22632301
{
22642302
debug!("try_alt_rcvr: pick candidate {:?}", pick);
@@ -2637,11 +2675,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26372675
name: Symbol::intern(&format!("{}_else", method_name.as_str())),
26382676
span: method_name.span,
26392677
};
2640-
let probe = self.lookup_probe(
2678+
let probe = self.lookup_probe_for_diagnostic(
26412679
new_name,
26422680
self_ty,
26432681
self_expr,
26442682
ProbeScope::TraitsInScope,
2683+
Some(expected),
26452684
);
26462685

26472686
// check the method arguments number
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// edition:2021
2+
3+
// Test that we do not suggest `.await` when it doesn't make sense.
4+
5+
struct A;
6+
7+
impl A {
8+
fn test(&self) -> i32 {
9+
1
10+
}
11+
}
12+
13+
async fn foo() -> A {
14+
A
15+
}
16+
17+
async fn async_main() {
18+
let x: u32 = foo().test();
19+
//~^ ERROR no method named `test` found for opaque type `impl Future<Output = A>` in the current scope
20+
}
21+
22+
fn main() {
23+
let _ = async_main();
24+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0599]: no method named `test` found for opaque type `impl Future<Output = A>` in the current scope
2+
--> $DIR/dont-suggest-await-on-method-return-mismatch.rs:18:24
3+
|
4+
LL | let x: u32 = foo().test();
5+
| ^^^^ method not found in `impl Future<Output = A>`
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0599`.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
struct Wrapper<T>(T);
2+
3+
impl Wrapper<Option<i32>> {
4+
fn inner_mut(&self) -> Option<&mut i32> {
5+
self.as_mut()
6+
//~^ ERROR no method named `as_mut` found for reference `&Wrapper<Option<i32>>` in the current scope
7+
//~| HELP one of the expressions' fields has a method of the same name
8+
//~| HELP items from traits can only be used if
9+
}
10+
11+
fn inner_mut_bad(&self) -> Option<&mut u32> {
12+
self.as_mut()
13+
//~^ ERROR no method named `as_mut` found for reference `&Wrapper<Option<i32>>` in the current scope
14+
//~| HELP items from traits can only be used if
15+
}
16+
}
17+
18+
fn main() {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0599]: no method named `as_mut` found for reference `&Wrapper<Option<i32>>` in the current scope
2+
--> $DIR/field-method-suggestion-using-return-ty.rs:5:14
3+
|
4+
LL | self.as_mut()
5+
| ^^^^^^ method not found in `&Wrapper<Option<i32>>`
6+
|
7+
= help: items from traits can only be used if the trait is implemented and in scope
8+
= note: the following trait defines an item `as_mut`, perhaps you need to implement it:
9+
candidate #1: `AsMut`
10+
help: one of the expressions' fields has a method of the same name
11+
|
12+
LL | self.0.as_mut()
13+
| ++
14+
15+
error[E0599]: no method named `as_mut` found for reference `&Wrapper<Option<i32>>` in the current scope
16+
--> $DIR/field-method-suggestion-using-return-ty.rs:12:14
17+
|
18+
LL | self.as_mut()
19+
| ^^^^^^ method not found in `&Wrapper<Option<i32>>`
20+
|
21+
= help: items from traits can only be used if the trait is implemented and in scope
22+
= note: the following trait defines an item `as_mut`, perhaps you need to implement it:
23+
candidate #1: `AsMut`
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0599`.

‎src/test/ui/privacy/private-field-ty-err.stderr

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,6 @@ error[E0616]: field `len` of struct `Foo` is private
33
|
44
LL | if x.len {
55
| ^^^ private field
6-
|
7-
help: a method `len` also exists, call it with parentheses
8-
|
9-
LL | if x.len() {
10-
| ++
116

127
error: aborting due to previous error
138

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// run-rustfix
2+
3+
#![allow(unused)]
4+
5+
fn as_ref() -> Option<Vec<u8>> {
6+
None
7+
}
8+
struct Type {
9+
option: Option<Vec<u8>>
10+
}
11+
trait Trait {
12+
fn foo(&self) -> &Vec<u8>;
13+
}
14+
impl Trait for Option<Vec<u8>> {
15+
fn foo(&self) -> &Vec<u8> {
16+
self.as_ref().unwrap()
17+
}
18+
}
19+
20+
impl Type {
21+
fn method(&self) -> Option<&Vec<u8>> {
22+
self.option.as_ref().map(|x| x)
23+
//~^ ERROR E0308
24+
}
25+
fn method2(&self) -> Option<&u8> {
26+
self.option.foo().get(0)
27+
//~^ ERROR E0425
28+
//~| ERROR E0308
29+
}
30+
}
31+
32+
fn main() {
33+
let _ = Type { option: None }.method();
34+
}

‎src/test/ui/suggestions/method-access-to-range-literal-typo.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
1+
// run-rustfix
2+
3+
#![allow(unused)]
4+
15
fn as_ref() -> Option<Vec<u8>> {
26
None
37
}
48
struct Type {
59
option: Option<Vec<u8>>
610
}
711
trait Trait {
8-
fn foo(&self) -> Vec<u8>;
12+
fn foo(&self) -> &Vec<u8>;
913
}
1014
impl Trait for Option<Vec<u8>> {
11-
fn foo(&self) -> Vec<u8> {
12-
vec![1, 2, 3]
15+
fn foo(&self) -> &Vec<u8> {
16+
self.as_ref().unwrap()
1317
}
1418
}
1519

1620
impl Type {
17-
fn method(&self) -> Option<Vec<u8>> {
21+
fn method(&self) -> Option<&Vec<u8>> {
1822
self.option..as_ref().map(|x| x)
1923
//~^ ERROR E0308
2024
}
21-
fn method2(&self) -> &u8 {
25+
fn method2(&self) -> Option<&u8> {
2226
self.option..foo().get(0)
2327
//~^ ERROR E0425
2428
//~| ERROR E0308

‎src/test/ui/suggestions/method-access-to-range-literal-typo.stderr

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0425]: cannot find function `foo` in this scope
2-
--> $DIR/method-access-to-range-literal-typo.rs:22:22
2+
--> $DIR/method-access-to-range-literal-typo.rs:26:22
33
|
44
LL | self.option..foo().get(0)
55
| ^^^ not found in this scope
@@ -11,31 +11,31 @@ LL + self.option.foo().get(0)
1111
|
1212

1313
error[E0308]: mismatched types
14-
--> $DIR/method-access-to-range-literal-typo.rs:18:9
14+
--> $DIR/method-access-to-range-literal-typo.rs:22:9
1515
|
16-
LL | fn method(&self) -> Option<Vec<u8>> {
17-
| --------------- expected `Option<Vec<u8>>` because of return type
16+
LL | fn method(&self) -> Option<&Vec<u8>> {
17+
| ---------------- expected `Option<&Vec<u8>>` because of return type
1818
LL | self.option..as_ref().map(|x| x)
1919
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found struct `Range`
2020
|
21-
= note: expected enum `Option<_>`
22-
found struct `std::ops::Range<Option<_>>`
21+
= note: expected enum `Option<&Vec<u8>>`
22+
found struct `std::ops::Range<Option<Vec<u8>>>`
2323
help: you likely meant to write a method call instead of a range
2424
|
2525
LL - self.option..as_ref().map(|x| x)
2626
LL + self.option.as_ref().map(|x| x)
2727
|
2828

2929
error[E0308]: mismatched types
30-
--> $DIR/method-access-to-range-literal-typo.rs:22:9
30+
--> $DIR/method-access-to-range-literal-typo.rs:26:9
3131
|
32-
LL | fn method2(&self) -> &u8 {
33-
| --- expected `&u8` because of return type
32+
LL | fn method2(&self) -> Option<&u8> {
33+
| ----------- expected `Option<&u8>` because of return type
3434
LL | self.option..foo().get(0)
35-
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found struct `Range`
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found struct `Range`
3636
|
37-
= note: expected reference `&u8`
38-
found struct `std::ops::Range<Option<Vec<u8>>>`
37+
= note: expected enum `Option<&u8>`
38+
found struct `std::ops::Range<Option<Vec<u8>>>`
3939
help: you likely meant to write a method call instead of a range
4040
|
4141
LL - self.option..foo().get(0)

0 commit comments

Comments
 (0)
Please sign in to comment.