]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_hir_typeck/src/demand.rs
Auto merge of #106696 - kylematsuda:early-binder, r=lcnr
[rust.git] / compiler / rustc_hir_typeck / src / demand.rs
index f68a428d09ae3ba4b1d7f4e5043d7c0b71bc064c..665dc8b6a2f2a4ed18091fb4952e550ca82ba30c 100644 (file)
@@ -85,6 +85,7 @@ pub fn emit_coerce_suggestions(
         self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
         self.check_for_range_as_method_call(err, expr, expr_ty, expected);
         self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
+        self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty);
     }
 
     /// Requires that the two types unify, and prints an error message if
@@ -303,11 +304,12 @@ fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
                 // 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 +559,19 @@ fn annotate_alternative_method_deref(
         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 +583,7 @@ fn annotate_alternative_method_deref(
         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 +1835,7 @@ pub fn check_for_cast(
     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 +1853,14 @@ pub fn check_for_range_as_method_call(
             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`.
+            // `tests/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 +1876,12 @@ pub fn check_for_range_as_method_call(
         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);
@@ -1936,4 +1942,77 @@ fn check_for_binding_assigned_block_without_tail_expression(
             err.span_label(block.span, "this block is missing a tail expression");
         }
     }
+
+    fn check_wrong_return_type_due_to_generic_arg(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        checked_ty: Ty<'tcx>,
+    ) {
+        let Some(hir::Node::Expr(parent_expr)) = self.tcx.hir().find_parent(expr.hir_id) else { return; };
+        enum CallableKind {
+            Function,
+            Method,
+            Constructor,
+        }
+        let mut maybe_emit_help = |def_id: hir::def_id::DefId,
+                                   callable: rustc_span::symbol::Ident,
+                                   args: &[hir::Expr<'_>],
+                                   kind: CallableKind| {
+            let arg_idx = args.iter().position(|a| a.hir_id == expr.hir_id).unwrap();
+            let fn_ty = self.tcx.bound_type_of(def_id).0;
+            if !fn_ty.is_fn() {
+                return;
+            }
+            let fn_sig = fn_ty.fn_sig(self.tcx).skip_binder();
+            let Some(&arg) = fn_sig.inputs().get(arg_idx + if matches!(kind, CallableKind::Method) { 1 } else { 0 }) else { return; };
+            if matches!(arg.kind(), ty::Param(_))
+                && fn_sig.output().contains(arg)
+                && self.node_ty(args[arg_idx].hir_id) == checked_ty
+            {
+                let mut multi_span: MultiSpan = parent_expr.span.into();
+                multi_span.push_span_label(
+                    args[arg_idx].span,
+                    format!(
+                        "this argument influences the {} of `{}`",
+                        if matches!(kind, CallableKind::Constructor) {
+                            "type"
+                        } else {
+                            "return type"
+                        },
+                        callable
+                    ),
+                );
+                err.span_help(
+                    multi_span,
+                    format!(
+                        "the {} `{}` due to the type of the argument passed",
+                        match kind {
+                            CallableKind::Function => "return type of this call is",
+                            CallableKind::Method => "return type of this call is",
+                            CallableKind::Constructor => "type constructed contains",
+                        },
+                        checked_ty
+                    ),
+                );
+            }
+        };
+        match parent_expr.kind {
+            hir::ExprKind::Call(fun, args) => {
+                let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = fun.kind else { return; };
+                let hir::def::Res::Def(kind, def_id) = path.res else { return; };
+                let callable_kind = if matches!(kind, hir::def::DefKind::Ctor(_, _)) {
+                    CallableKind::Constructor
+                } else {
+                    CallableKind::Function
+                };
+                maybe_emit_help(def_id, path.segments[0].ident, args, callable_kind);
+            }
+            hir::ExprKind::MethodCall(method, _receiver, args, _span) => {
+                let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(parent_expr.hir_id) else { return; };
+                maybe_emit_help(def_id, method.ident, args, CallableKind::Method)
+            }
+            _ => return,
+        }
+    }
 }