about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl4
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs39
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs114
-rw-r--r--tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr2
-rw-r--r--tests/ui/suggestions/copied-and-cloned.fixed2
-rw-r--r--tests/ui/suggestions/copied-and-cloned.rs2
-rw-r--r--tests/ui/suggestions/copied-and-cloned.stderr2
7 files changed, 93 insertions, 72 deletions
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index 3d012a15a67..2281343e250 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -77,6 +77,10 @@ hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang
 
 hir_typeck_op_trait_generic_params = `{$method_name}` must not have any generic parameters
 
+hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expected_ty}` to `{$expr_ty}`
+hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}`
+hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}`
+
 hir_typeck_return_stmt_outside_of_fn_body =
     {$statement_kind} statement outside of function body
     .encl_body_label = the {$statement_kind} is part of this body...
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 05906a4b9f5..36096aa35d4 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -252,6 +252,45 @@ impl HelpUseLatestEdition {
     }
 }
 
+#[derive(Subdiagnostic)]
+pub enum OptionResultRefMismatch<'tcx> {
+    #[suggestion(
+        hir_typeck_option_result_copied,
+        code = ".copied()",
+        style = "verbose",
+        applicability = "machine-applicable"
+    )]
+    Copied {
+        #[primary_span]
+        span: Span,
+        def_path: String,
+    },
+    #[suggestion(
+        hir_typeck_option_result_cloned,
+        code = ".cloned()",
+        style = "verbose",
+        applicability = "machine-applicable"
+    )]
+    Cloned {
+        #[primary_span]
+        span: Span,
+        def_path: String,
+    },
+    #[suggestion(
+        hir_typeck_option_result_asref,
+        code = ".as_ref()",
+        style = "verbose",
+        applicability = "machine-applicable"
+    )]
+    AsRef {
+        #[primary_span]
+        span: Span,
+        def_path: String,
+        expected_ty: Ty<'tcx>,
+        expr_ty: Ty<'tcx>,
+    },
+}
+
 #[derive(Diagnostic)]
 #[diag(hir_typeck_const_select_must_be_const)]
 #[help]
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 4a1cbaceaf7..ff715a1999d 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1,8 +1,6 @@
 use super::FnCtxt;
 
-use crate::errors::{
-    AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing, SuggestConvertViaMethod,
-};
+use crate::errors;
 use crate::fluent_generated as fluent;
 use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
 use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
@@ -434,7 +432,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`,
             // but those checks need to be a bit more delicate and the benefit is diminishing.
             if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
-                err.subdiagnostic(SuggestConvertViaMethod {
+                err.subdiagnostic(errors::SuggestConvertViaMethod {
                     span: expr.span.shrink_to_hi(),
                     sugg: ".as_ref()",
                     expected,
@@ -447,7 +445,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 && self.can_eq(self.param_env, deref_ty, peeled)
                 && error_tys_equate_as_ref
             {
-                err.subdiagnostic(SuggestConvertViaMethod {
+                err.subdiagnostic(errors::SuggestConvertViaMethod {
                     span: expr.span.shrink_to_hi(),
                     sugg: ".as_deref()",
                     expected,
@@ -521,7 +519,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if self.can_coerce(Ty::new_box(self.tcx, found), expected) {
             let suggest_boxing = match found.kind() {
                 ty::Tuple(tuple) if tuple.is_empty() => {
-                    SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
+                    errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
                 }
                 ty::Generator(def_id, ..)
                     if matches!(
@@ -529,9 +527,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         Some(GeneratorKind::Async(AsyncGeneratorKind::Closure))
                     ) =>
                 {
-                    SuggestBoxing::AsyncBody
+                    errors::SuggestBoxing::AsyncBody
                 }
-                _ => SuggestBoxing::Other { start: span.shrink_to_lo(), end: span.shrink_to_hi() },
+                _ => errors::SuggestBoxing::Other {
+                    start: span.shrink_to_lo(),
+                    end: span.shrink_to_hi(),
+                },
             };
             err.subdiagnostic(suggest_boxing);
 
@@ -756,23 +757,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         match &fn_decl.output {
             &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => {
                 // `fn main()` must return `()`, do not suggest changing return type
-                err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span });
+                err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit { span });
                 return true;
             }
             &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
                 if let Some(found) = found.make_suggestable(self.tcx, false) {
-                    err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
+                    err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() });
                     return true;
                 } else if let ty::Closure(_, args) = found.kind()
                     // FIXME(compiler-errors): Get better at printing binders...
                     && let closure = args.as_closure()
                     && closure.sig().is_suggestable(self.tcx, false)
                 {
-                    err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() });
+                    err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() });
                     return true;
                 } else {
                     // FIXME: if `found` could be `impl Iterator` we should suggest that.
-                    err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
+                    err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere { span });
                     return true
                 }
             }
@@ -794,10 +795,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     debug!(?found);
                     if found.is_suggestable(self.tcx, false) {
                         if term.span.is_empty() {
-                            err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
+                            err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() });
                             return true;
                         } else {
-                            err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
+                            err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other { span, expected });
                         }
                     }
                 }
@@ -813,7 +814,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let ty = self.normalize(span, ty);
                 let ty = self.tcx.erase_late_bound_regions(ty);
                 if self.can_coerce(expected, ty) {
-                    err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
+                    err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other { span, expected });
                     self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
                     return true;
                 }
@@ -1103,65 +1104,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return false;
         }
 
-        let mut suggest_copied_cloned_or_as_ref = || {
+        if Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Result)
+            && self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
+            || Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Option)
+        {
             let expr_inner_ty = args.type_at(0);
             let expected_inner_ty = expected_args.type_at(0);
             if let &ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
-                && self.can_eq(self.param_env, ty, expected_inner_ty)
-            {
-                let def_path = self.tcx.def_path_str(adt_def.did());
-                if self.type_is_copy_modulo_regions(self.param_env, ty) {
-                    diag.span_suggestion_verbose(
-                        expr.span.shrink_to_hi(),
-                        format!(
-                            "use `{def_path}::copied` to copy the value inside the `{def_path}`"
-                        ),
-                        ".copied()",
-                        Applicability::MachineApplicable,
-                    );
-                    return true;
-                } else if let Some(expected_ty_expr) = expected_ty_expr {
-                    diag.span_suggestion_verbose(
-                        expected_ty_expr.span.shrink_to_hi(),
-                        format!(
-                            "use `{def_path}::as_ref()` to convert `{expected_ty}` to `{expr_ty}`"
-                        ),
-                        ".as_ref()",
-                        Applicability::MachineApplicable,
-                    );
-                    return true;
-                } else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
-                    && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
-                        self,
-                        self.param_env,
-                        ty,
-                        clone_did,
-                    )
+                    && self.can_eq(self.param_env, ty, expected_inner_ty)
                 {
-                    diag.span_suggestion_verbose(
-                        expr.span.shrink_to_hi(),
-                        format!(
-                            "use `{def_path}::cloned` to clone the value inside the `{def_path}`"
-                        ),
-                        ".cloned()",
-                        Applicability::MachineApplicable,
-                    );
+                    let def_path = self.tcx.def_path_str(adt_def.did());
+                    let span = expr.span.shrink_to_hi();
+                    let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) {
+                        errors::OptionResultRefMismatch::Copied {
+                            span, def_path
+                        }
+                    } else if let Some(expected_ty_expr) = expected_ty_expr {
+                        errors::OptionResultRefMismatch::AsRef {
+                            span: expected_ty_expr.span.shrink_to_hi(), expected_ty, expr_ty, def_path
+                        }
+                    } else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
+                        && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
+                            self,
+                            self.param_env,
+                            ty,
+                            clone_did,
+                        )
+                    {
+                        errors::OptionResultRefMismatch::Cloned {
+                            span, def_path
+                        }
+                    } else {
+                        return false;
+                    };
+                    diag.subdiagnostic(subdiag);
                     return true;
                 }
-            }
-            false
-        };
-
-        if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result)
-            && adt_def.did() == result_did
-            // Check that the error types are equal
-            && self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
-        {
-            return suggest_copied_cloned_or_as_ref();
-        } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
-            && adt_def.did() == option_did
-        {
-            return suggest_copied_cloned_or_as_ref();
         }
 
         false
diff --git a/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr b/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr
index c06c506a311..65d18761b18 100644
--- a/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr
+++ b/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr
@@ -6,7 +6,7 @@ LL |     debug_assert_eq!(iter.next(), Some(value));
    |
    = note: expected enum `Option<<I as Iterator>::Item>`
               found enum `Option<&<I as Iterator>::Item>`
-help: use `Option::as_ref()` to convert `Option<<I as Iterator>::Item>` to `Option<&<I as Iterator>::Item>`
+help: use `Option::as_ref` to convert `Option<<I as Iterator>::Item>` to `Option<&<I as Iterator>::Item>`
    |
 LL |     debug_assert_eq!(iter.next().as_ref(), Some(value));
    |                                 +++++++++
diff --git a/tests/ui/suggestions/copied-and-cloned.fixed b/tests/ui/suggestions/copied-and-cloned.fixed
index 13031f424cb..dec89c60388 100644
--- a/tests/ui/suggestions/copied-and-cloned.fixed
+++ b/tests/ui/suggestions/copied-and-cloned.fixed
@@ -26,6 +26,6 @@ fn main() {
     let y = Some(&s);
     println!("{}", x.as_ref() == y);
     //~^ ERROR mismatched types
-    //~| HELP use `Option::as_ref()` to convert `Option<String>` to `Option<&String>`
+    //~| HELP use `Option::as_ref` to convert `Option<String>` to `Option<&String>`
 
 }
diff --git a/tests/ui/suggestions/copied-and-cloned.rs b/tests/ui/suggestions/copied-and-cloned.rs
index 2927d66dea9..99485a2b525 100644
--- a/tests/ui/suggestions/copied-and-cloned.rs
+++ b/tests/ui/suggestions/copied-and-cloned.rs
@@ -26,6 +26,6 @@ fn main() {
     let y = Some(&s);
     println!("{}", x == y);
     //~^ ERROR mismatched types
-    //~| HELP use `Option::as_ref()` to convert `Option<String>` to `Option<&String>`
+    //~| HELP use `Option::as_ref` to convert `Option<String>` to `Option<&String>`
 
 }
diff --git a/tests/ui/suggestions/copied-and-cloned.stderr b/tests/ui/suggestions/copied-and-cloned.stderr
index 19aaf6e00b1..6384c9b55da 100644
--- a/tests/ui/suggestions/copied-and-cloned.stderr
+++ b/tests/ui/suggestions/copied-and-cloned.stderr
@@ -86,7 +86,7 @@ LL |     println!("{}", x == y);
    |
    = note: expected enum `Option<String>`
               found enum `Option<&String>`
-help: use `Option::as_ref()` to convert `Option<String>` to `Option<&String>`
+help: use `Option::as_ref` to convert `Option<String>` to `Option<&String>`
    |
 LL |     println!("{}", x.as_ref() == y);
    |                     +++++++++