about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-01-10 06:22:01 +0000
committerbors <bors@rust-lang.org>2023-01-10 06:22:01 +0000
commit0442fbabe24ec43636a80ad1f40a0ad92a2e38df (patch)
treea494ceba9b45768bd74fce08dfaceccf3bd0c902
parentdeba5ddd0776108c51a69844d0ffe3f96886dd52 (diff)
parent4f15034b55f6c9e134f7644c4571faa5ddb5facb (diff)
downloadrust-0442fbabe24ec43636a80ad1f40a0ad92a2e38df.tar.gz
rust-0442fbabe24ec43636a80ad1f40a0ad92a2e38df.zip
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.
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs9
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs25
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs22
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs80
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs66
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs77
-rw-r--r--src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.rs24
-rw-r--r--src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr9
-rw-r--r--src/test/ui/methods/field-method-suggestion-using-return-ty.rs18
-rw-r--r--src/test/ui/methods/field-method-suggestion-using-return-ty.stderr27
-rw-r--r--src/test/ui/privacy/private-field-ty-err.stderr5
-rw-r--r--src/test/ui/suggestions/method-access-to-range-literal-typo.fixed34
-rw-r--r--src/test/ui/suggestions/method-access-to-range-literal-typo.rs14
-rw-r--r--src/test/ui/suggestions/method-access-to-range-literal-typo.stderr24
16 files changed, 329 insertions, 107 deletions
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 2cb976f718c..3b664363d23 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -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;
             };
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index f68a428d09a..33fc7413a67 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -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);
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index b08b22108c8..ba1a5a0cb03 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -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,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index b7681d108ed..594a60c70a7 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -820,6 +820,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         SelfSource::QPath(qself),
                         error,
                         None,
+                        Expectation::NoExpectation,
                     ) {
                         e.emit();
                     }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 236bdc60e67..91498265259 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -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,
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index d276bcdb81e..146d5e60c2f 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -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,
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 2daf1979ee5..02b4d5bb2fb 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -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
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 536c4270659..62e80659486 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -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
diff --git a/src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.rs b/src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.rs
new file mode 100644
index 00000000000..f2f87a90817
--- /dev/null
+++ b/src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.rs
@@ -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();
+}
diff --git a/src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr b/src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr
new file mode 100644
index 00000000000..e65d9d0e5d3
--- /dev/null
+++ b/src/test/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr
@@ -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`.
diff --git a/src/test/ui/methods/field-method-suggestion-using-return-ty.rs b/src/test/ui/methods/field-method-suggestion-using-return-ty.rs
new file mode 100644
index 00000000000..07b975c44c9
--- /dev/null
+++ b/src/test/ui/methods/field-method-suggestion-using-return-ty.rs
@@ -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() {}
diff --git a/src/test/ui/methods/field-method-suggestion-using-return-ty.stderr b/src/test/ui/methods/field-method-suggestion-using-return-ty.stderr
new file mode 100644
index 00000000000..51c52a07e10
--- /dev/null
+++ b/src/test/ui/methods/field-method-suggestion-using-return-ty.stderr
@@ -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`.
diff --git a/src/test/ui/privacy/private-field-ty-err.stderr b/src/test/ui/privacy/private-field-ty-err.stderr
index e583a25fd8f..98ba7856e57 100644
--- a/src/test/ui/privacy/private-field-ty-err.stderr
+++ b/src/test/ui/privacy/private-field-ty-err.stderr
@@ -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
 
diff --git a/src/test/ui/suggestions/method-access-to-range-literal-typo.fixed b/src/test/ui/suggestions/method-access-to-range-literal-typo.fixed
new file mode 100644
index 00000000000..13601eef6c2
--- /dev/null
+++ b/src/test/ui/suggestions/method-access-to-range-literal-typo.fixed
@@ -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();
+}
diff --git a/src/test/ui/suggestions/method-access-to-range-literal-typo.rs b/src/test/ui/suggestions/method-access-to-range-literal-typo.rs
index ac662edafe6..fdcd6425d32 100644
--- a/src/test/ui/suggestions/method-access-to-range-literal-typo.rs
+++ b/src/test/ui/suggestions/method-access-to-range-literal-typo.rs
@@ -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
diff --git a/src/test/ui/suggestions/method-access-to-range-literal-typo.stderr b/src/test/ui/suggestions/method-access-to-range-literal-typo.stderr
index c84f9467891..f421408944b 100644
--- a/src/test/ui/suggestions/method-access-to-range-literal-typo.stderr
+++ b/src/test/ui/suggestions/method-access-to-range-literal-typo.stderr
@@ -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)