]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_hir_typeck/src/method/suggest.rs
Rollup merge of #101975 - chenyukang:fix-101749, r=compiler-errors
[rust.git] / compiler / rustc_hir_typeck / src / method / suggest.rs
index d0ea2b0e66475df861048ba32899c0e81cbaa0c3..9ba4ddfd5cf7fd37920e965bfe97bb23a0e137f9 100644 (file)
@@ -5,6 +5,7 @@
 use crate::FnCtxt;
 use rustc_ast::ast::Mutability;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::StashKey;
 use rustc_errors::{
     pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
     MultiSpan,
@@ -13,6 +14,8 @@
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
+use rustc_hir::PatKind::Binding;
+use rustc_hir::PathSegment;
 use rustc_hir::{ExprKind, Node, QPath};
 use rustc_infer::infer::{
     type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
     FulfillmentError, Obligation, ObligationCause, ObligationCauseCode,
 };
 
-use std::cmp::Ordering;
-use std::iter;
-
 use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
 use super::{CandidateSource, MethodError, NoMatchData};
+use rustc_hir::intravisit::Visitor;
+use std::cmp::Ordering;
+use std::iter;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
@@ -442,7 +445,7 @@ pub fn report_method_error(
                     let mut unimplemented_traits = FxHashMap::default();
                     let mut unimplemented_traits_only = true;
                     for (predicate, _parent_pred, cause) in &unsatisfied_predicates {
-                        if let (ty::PredicateKind::Trait(p), Some(cause)) =
+                        if let (ty::PredicateKind::Clause(ty::Clause::Trait(p)), Some(cause)) =
                             (predicate.kind().skip_binder(), cause.as_ref())
                         {
                             if p.trait_ref.self_ty() != rcvr_ty {
@@ -469,7 +472,7 @@ pub fn report_method_error(
                     // because of some non-Clone item being iterated over.
                     for (predicate, _parent_pred, _cause) in &unsatisfied_predicates {
                         match predicate.kind().skip_binder() {
-                            ty::PredicateKind::Trait(p)
+                            ty::PredicateKind::Clause(ty::Clause::Trait(p))
                                 if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {}
                             _ => {
                                 unimplemented_traits_only = false;
@@ -481,7 +484,7 @@ pub fn report_method_error(
                     let mut collect_type_param_suggestions =
                         |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| {
                             // We don't care about regions here, so it's fine to skip the binder here.
-                            if let (ty::Param(_), ty::PredicateKind::Trait(p)) =
+                            if let (ty::Param(_), ty::PredicateKind::Clause(ty::Clause::Trait(p))) =
                                 (self_ty.kind(), parent_pred.kind().skip_binder())
                             {
                                 let hir = self.tcx.hir();
@@ -544,7 +547,7 @@ pub fn report_method_error(
                     let mut format_pred = |pred: ty::Predicate<'tcx>| {
                         let bound_predicate = pred.kind();
                         match bound_predicate.skip_binder() {
-                            ty::PredicateKind::Projection(pred) => {
+                            ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
                                 let pred = bound_predicate.rebind(pred);
                                 // `<Foo as Iterator>::Item = String`.
                                 let projection_ty = pred.skip_binder().projection_ty;
@@ -567,7 +570,7 @@ pub fn report_method_error(
                                 bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
                                 Some((obligation, projection_ty.self_ty()))
                             }
-                            ty::PredicateKind::Trait(poly_trait_ref) => {
+                            ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => {
                                 let p = poly_trait_ref.trait_ref;
                                 let self_ty = p.self_ty();
                                 let path = p.print_only_trait_path();
@@ -637,7 +640,7 @@ pub fn report_method_error(
                                 let sized_pred =
                                     unsatisfied_predicates.iter().any(|(pred, _, _)| {
                                         match pred.kind().skip_binder() {
-                                            ty::PredicateKind::Trait(pred) => {
+                                            ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
                                                 Some(pred.def_id())
                                                     == self.tcx.lang_items().sized_trait()
                                                     && pred.polarity == ty::ImplPolarity::Positive
@@ -1147,19 +1150,11 @@ fn suggest_associated_call_syntax(
                 && assoc.kind == ty::AssocKind::Fn
             {
                 let sig = self.tcx.fn_sig(assoc.def_id);
-                if let Some(first) = sig.inputs().skip_binder().get(0) {
-                    if first.peel_refs() == rcvr_ty.peel_refs() {
-                        None
-                    } else {
-                        Some(if first.is_region_ptr() {
-                            if first.is_mutable_ptr() { "&mut " } else { "&" }
-                        } else {
-                            ""
-                        })
-                    }
-                } else {
+                sig.inputs().skip_binder().get(0).and_then(|first| if first.peel_refs() == rcvr_ty.peel_refs() {
                     None
-                }
+                } else {
+                    Some(first.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()))
+                })
             } else {
                 None
             };
@@ -1470,6 +1465,61 @@ fn suggest_constraining_numerical_ty(
         false
     }
 
+    /// For code `rect::area(...)`,
+    /// if `rect` is a local variable and `area` is a valid assoc method for it,
+    /// we try to suggest `rect.area()`
+    pub(crate) fn suggest_assoc_method_call(&self, segs: &[PathSegment<'_>]) {
+        debug!("suggest_assoc_method_call segs: {:?}", segs);
+        let [seg1, seg2] = segs else { return; };
+        let Some(mut diag) =
+                self.tcx.sess.diagnostic().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod)
+                else { return };
+
+        let map = self.infcx.tcx.hir();
+        let body = map.body(rustc_hir::BodyId { hir_id: self.body_id });
+        struct LetVisitor<'a> {
+            result: Option<&'a hir::Expr<'a>>,
+            ident_name: Symbol,
+        }
+
+        impl<'v> Visitor<'v> for LetVisitor<'v> {
+            fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
+                if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind {
+                    if let Binding(_, _, ident, ..) = pat.kind &&
+                        ident.name == self.ident_name {
+                        self.result = *init;
+                    }
+                }
+                hir::intravisit::walk_stmt(self, ex);
+            }
+        }
+
+        let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name };
+        visitor.visit_body(&body);
+
+        let parent = self.tcx.hir().get_parent_node(seg1.hir_id);
+        if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) &&
+            let Some(expr) = visitor.result {
+            let self_ty = self.node_ty(expr.hir_id);
+            let probe = self.lookup_probe(
+                seg2.ident,
+                self_ty,
+                call_expr,
+                ProbeScope::TraitsInScope,
+            );
+            if probe.is_ok() {
+                let sm = self.infcx.tcx.sess.source_map();
+                diag.span_suggestion_verbose(
+                    sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(),
+                    "you may have meant to call an instance method",
+                    ".".to_string(),
+                    Applicability::MaybeIncorrect
+                );
+            }
+        }
+        diag.emit();
+    }
+
     /// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()`
     fn suggest_calling_method_on_field(
         &self,
@@ -1722,7 +1772,7 @@ pub(crate) fn note_unmet_impls_on_type(
     ) {
         let all_local_types_needing_impls =
             errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() {
-                ty::PredicateKind::Trait(pred) => match pred.self_ty().kind() {
+                ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => match pred.self_ty().kind() {
                     ty::Adt(def, _) => def.did().is_local(),
                     _ => false,
                 },
@@ -1731,7 +1781,7 @@ pub(crate) fn note_unmet_impls_on_type(
         let mut preds: Vec<_> = errors
             .iter()
             .filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
-                ty::PredicateKind::Trait(pred) => Some(pred),
+                ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => Some(pred),
                 _ => None,
             })
             .collect();
@@ -1802,7 +1852,7 @@ fn suggest_derive(
         let mut derives = Vec::<(String, Span, Symbol)>::new();
         let mut traits = Vec::<Span>::new();
         for (pred, _, _) in unsatisfied_predicates {
-            let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue };
+            let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = pred.kind().skip_binder() else { continue };
             let adt = match trait_pred.self_ty().ty_adt_def() {
                 Some(adt) if adt.did().is_local() => adt,
                 _ => continue,
@@ -1960,7 +2010,7 @@ fn suggest_await_before_method(
         span: Span,
     ) {
         let output_ty = match self.get_impl_future_output_ty(ty) {
-            Some(output_ty) => self.resolve_vars_if_possible(output_ty).skip_binder(),
+            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);
@@ -2212,8 +2262,10 @@ fn suggest_traits_to_import(
                     match p.kind().skip_binder() {
                         // Hide traits if they are present in predicates as they can be fixed without
                         // having to implement them.
-                        ty::PredicateKind::Trait(t) => t.def_id() == info.def_id,
-                        ty::PredicateKind::Projection(p) => {
+                        ty::PredicateKind::Clause(ty::Clause::Trait(t)) => {
+                            t.def_id() == info.def_id
+                        }
+                        ty::PredicateKind::Clause(ty::Clause::Projection(p)) => {
                             p.projection_ty.item_def_id == info.def_id
                         }
                         _ => false,
@@ -2625,11 +2677,7 @@ fn print_disambiguation_help<'tcx>(
     let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) {
         let args = format!(
             "({}{})",
-            if rcvr_ty.is_region_ptr() {
-                if rcvr_ty.is_mutable_ptr() { "&mut " } else { "&" }
-            } else {
-                ""
-            },
+            rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()),
             std::iter::once(receiver)
                 .chain(args.iter())
                 .map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| {