]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
New upstream version 1.62.1+dfsg1
[rustc.git] / compiler / rustc_typeck / src / check / fn_ctxt / suggestions.rs
index 68d555b3a651d224a2dbb0f16592b916aed2d0b2..bd58a6754481a53e7ae6168ab4439ee4f2b2c5d9 100644 (file)
@@ -1,10 +1,9 @@
 use super::FnCtxt;
 use crate::astconv::AstConv;
+use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
 
 use rustc_ast::util::parser::ExprPrecedence;
-use rustc_span::{self, MultiSpan, Span};
-
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_errors::{Applicability, Diagnostic, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind};
 use rustc_hir::lang_items::LangItem;
@@ -13,12 +12,14 @@ use rustc_hir::{
     WherePredicate,
 };
 use rustc_infer::infer::{self, TyCtxtInferExt};
-
+use rustc_infer::traits;
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Binder, Ty};
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, Binder, ToPredicate, Ty};
 use rustc_span::symbol::{kw, sym};
+use rustc_span::Span;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 
-use rustc_middle::ty::subst::GenericArgKind;
 use std::iter;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -71,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
     /// the ctor would successfully solve the type mismatch and if so, suggest it:
-    /// ```
+    /// ```compile_fail,E0308
     /// fn foo(x: usize) -> usize { x }
     /// let x: usize = foo;  // suggest calling the `foo` function: `foo(42)`
     /// ```
@@ -150,7 +151,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
                 Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
                     sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
-                    match def_id.as_local().map(|def_id| hir.def_kind(def_id)) {
+                    match def_id.as_local().map(|def_id| self.tcx.def_kind(def_id)) {
                         Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
                             msg = "instantiate this tuple variant";
                         }
@@ -462,7 +463,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     /// A common error is to forget to add a semicolon at the end of a block, e.g.,
     ///
-    /// ```
+    /// ```compile_fail,E0308
+    /// # fn bar_that_returns_u32() -> u32 { 4 }
     /// fn foo() {
     ///     bar_that_returns_u32()
     /// }
@@ -503,7 +505,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     /// A possible error is to forget to add a return type that is needed:
     ///
-    /// ```
+    /// ```compile_fail,E0308
+    /// # fn bar_that_returns_u32() -> u32 { 4 }
     /// fn foo() {
     ///     bar_that_returns_u32()
     /// }
@@ -525,30 +528,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
         // Only suggest changing the return type for methods that
         // haven't set a return type at all (and aren't `fn main()` or an impl).
-        match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
+        match (&fn_decl.output, found.is_suggestable(self.tcx), can_suggest, expected.is_unit()) {
             (&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
-                err.span_suggestion(
-                    span,
-                    "try adding a return type",
-                    format!("-> {} ", found),
-                    Applicability::MachineApplicable,
-                );
+                err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found });
                 true
             }
             (&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
                 // FIXME: if `found` could be `impl Iterator` or `impl Fn*`, we should suggest
                 // that.
-                err.span_suggestion(
-                    span,
-                    "a return type might be missing here",
-                    "-> _ ".to_string(),
-                    Applicability::HasPlaceholders,
-                );
+                err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
                 true
             }
             (&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
                 // `fn main()` must return `()`, do not suggest changing return type
-                err.span_label(span, "expected `()` because of default return type");
+                err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span });
                 true
             }
             // expectation was caused by something else, not the default return
@@ -557,16 +550,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Only point to return type if the expected type is the return type, as if they
                 // are not, the expectation must have been caused by something else.
                 debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
-                let sp = ty.span;
+                let span = ty.span;
                 let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
                 debug!("suggest_missing_return_type: return type {:?}", ty);
                 debug!("suggest_missing_return_type: expected type {:?}", ty);
                 let bound_vars = self.tcx.late_bound_vars(fn_id);
                 let ty = Binder::bind_with_vars(ty, bound_vars);
-                let ty = self.normalize_associated_types_in(sp, ty);
+                let ty = self.normalize_associated_types_in(span, ty);
                 let ty = self.tcx.erase_late_bound_regions(ty);
                 if self.can_coerce(expected, ty) {
-                    err.span_label(sp, format!("expected `{}` because of return type", expected));
+                    err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
                     self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
                     return true;
                 }
@@ -578,7 +571,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// check whether the return type is a generic type with a trait bound
     /// only suggest this if the generic param is not present in the arguments
     /// if this is true, hint them towards changing the return type to `impl Trait`
-    /// ```
+    /// ```compile_fail,E0308
     /// fn cant_name_it<T: Fn() -> u32>() -> T {
     ///     || 3
     /// }
@@ -608,17 +601,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             kind:
                 hir::ItemKind::Fn(
                     hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. },
-                    hir::Generics { params, where_clause, .. },
+                    hir::Generics { params, predicates, .. },
                     _body_id,
                 ),
             ..
         })) = fn_node else { return };
 
-        let Some(expected_generic_param) = params.get(expected_ty_as_param.index as usize) else { return };
+        if params.get(expected_ty_as_param.index as usize).is_none() {
+            return;
+        };
 
         // get all where BoundPredicates here, because they are used in to cases below
-        let where_predicates = where_clause
-            .predicates
+        let where_predicates = predicates
             .iter()
             .filter_map(|p| match p {
                 WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
@@ -646,13 +640,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // now get all predicates in the same types as the where bounds, so we can chain them
         let predicates_from_where =
-            where_predicates.iter().flatten().map(|bounds| bounds.iter()).flatten();
+            where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
 
         // extract all bounds from the source code using their spans
-        let all_matching_bounds_strs = expected_generic_param
-            .bounds
-            .iter()
-            .chain(predicates_from_where)
+        let all_matching_bounds_strs = predicates_from_where
             .filter_map(|bound| match bound {
                 GenericBound::Trait(_, _) => {
                     self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
@@ -846,4 +837,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let node = self.tcx.hir().get(id);
         matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
     }
+
+    /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,
+    /// which is a side-effect of autoref.
+    pub(crate) fn note_type_is_not_clone(
+        &self,
+        diag: &mut Diagnostic,
+        expected_ty: Ty<'tcx>,
+        found_ty: Ty<'tcx>,
+        expr: &hir::Expr<'_>,
+    ) {
+        let hir::ExprKind::MethodCall(segment, &[ref callee_expr], _) = expr.kind else { return; };
+        let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; };
+        let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
+        let results = self.typeck_results.borrow();
+        // First, look for a `Clone::clone` call
+        if segment.ident.name == sym::clone
+            && results.type_dependent_def_id(expr.hir_id).map_or(
+                false,
+                |did| {
+                    self.tcx.associated_item(did).container
+                        == ty::AssocItemContainer::TraitContainer(clone_trait_did)
+                },
+            )
+            // If that clone call hasn't already dereferenced the self type (i.e. don't give this
+            // diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
+            && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
+            // Check that we're in fact trying to clone into the expected type
+            && self.can_coerce(*pointee_ty, expected_ty)
+            // And the expected type doesn't implement `Clone`
+            && !self.predicate_must_hold_considering_regions(&traits::Obligation {
+                cause: traits::ObligationCause::dummy(),
+                param_env: self.param_env,
+                recursion_depth: 0,
+                predicate: ty::Binder::dummy(ty::TraitRef {
+                    def_id: clone_trait_did,
+                    substs: self.tcx.mk_substs([expected_ty.into()].iter()),
+                })
+                .without_const()
+                .to_predicate(self.tcx),
+            })
+        {
+            diag.span_note(
+                callee_expr.span,
+                &format!(
+                    "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
+                ),
+            );
+        }
+    }
 }