Skip to content

Commit 0442fba

Browse files
committed
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

+4-5
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

+15-10
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

+16-6
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

+1
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

+1
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

+54-26
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,

0 commit comments

Comments
 (0)