about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs41
-rw-r--r--tests/ui/return/return-from-residual-sugg-issue-125997.fixed19
-rw-r--r--tests/ui/return/return-from-residual-sugg-issue-125997.rs15
-rw-r--r--tests/ui/return/return-from-residual-sugg-issue-125997.stderr43
4 files changed, 111 insertions, 7 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index d0f76f0d50e..a962be54c3d 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -4610,6 +4610,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         })
     }
 
+    // For E0277 when use `?` operator, suggest adding
+    // a suitable return type in `FnSig`, and a default
+    // return value at the end of the function's body.
     pub(super) fn suggest_add_result_as_return_type(
         &self,
         obligation: &PredicateObligation<'tcx>,
@@ -4620,19 +4623,47 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             return;
         }
 
+        // Only suggest for local function and associated method,
+        // because this suggest adding both return type in
+        // the `FnSig` and a default return value in the body, so it
+        // is not suitable for foreign function without a local body,
+        // and neighter for trait method which may be also implemented
+        // in other place, so shouldn't change it's FnSig.
+        fn choose_suggest_items<'tcx, 'hir>(
+            tcx: TyCtxt<'tcx>,
+            node: hir::Node<'hir>,
+        ) -> Option<(&'hir hir::FnDecl<'hir>, hir::BodyId)> {
+            match node {
+                hir::Node::Item(item) if let hir::ItemKind::Fn(sig, _, body_id) = item.kind => {
+                    Some((sig.decl, body_id))
+                }
+                hir::Node::ImplItem(item)
+                    if let hir::ImplItemKind::Fn(sig, body_id) = item.kind =>
+                {
+                    let parent = tcx.parent_hir_node(item.hir_id());
+                    if let hir::Node::Item(item) = parent
+                        && let hir::ItemKind::Impl(imp) = item.kind
+                        && imp.of_trait.is_none()
+                    {
+                        return Some((sig.decl, body_id));
+                    }
+                    None
+                }
+                _ => None,
+            }
+        }
+
         let node = self.tcx.hir_node_by_def_id(obligation.cause.body_id);
-        if let hir::Node::Item(item) = node
-            && let hir::ItemKind::Fn(sig, _, body_id) = item.kind
-            && let hir::FnRetTy::DefaultReturn(ret_span) = sig.decl.output
+        if let Some((fn_decl, body_id)) = choose_suggest_items(self.tcx, node)
+            && let hir::FnRetTy::DefaultReturn(ret_span) = fn_decl.output
             && self.tcx.is_diagnostic_item(sym::FromResidual, trait_pred.def_id())
             && trait_pred.skip_binder().trait_ref.args.type_at(0).is_unit()
             && let ty::Adt(def, _) = trait_pred.skip_binder().trait_ref.args.type_at(1).kind()
             && self.tcx.is_diagnostic_item(sym::Result, def.did())
         {
-            let body = self.tcx.hir().body(body_id);
             let mut sugg_spans =
                 vec![(ret_span, " -> Result<(), Box<dyn std::error::Error>>".to_string())];
-
+            let body = self.tcx.hir().body(body_id);
             if let hir::ExprKind::Block(b, _) = body.value.kind
                 && b.expr.is_none()
             {
diff --git a/tests/ui/return/return-from-residual-sugg-issue-125997.fixed b/tests/ui/return/return-from-residual-sugg-issue-125997.fixed
index b2eca69aeb9..a5a13399825 100644
--- a/tests/ui/return/return-from-residual-sugg-issue-125997.fixed
+++ b/tests/ui/return/return-from-residual-sugg-issue-125997.fixed
@@ -33,6 +33,25 @@ macro_rules! mac {
     };
 }
 
+struct A;
+
+impl A {
+    fn test4(&self) -> Result<(), Box<dyn std::error::Error>> {
+        let mut _file = File::create("foo.txt")?;
+        //~^ ERROR the `?` operator can only be used in a method
+    
+    Ok(())
+}
+
+    fn test5(&self) -> Result<(), Box<dyn std::error::Error>> {
+        let mut _file = File::create("foo.txt")?;
+        //~^ ERROR the `?` operator can only be used in a method
+        println!();
+    
+    Ok(())
+}
+}
+
 fn main() -> Result<(), Box<dyn std::error::Error>> {
     let mut _file = File::create("foo.txt")?;
     //~^ ERROR the `?` operator can only be used in a function
diff --git a/tests/ui/return/return-from-residual-sugg-issue-125997.rs b/tests/ui/return/return-from-residual-sugg-issue-125997.rs
index dd8550a388b..30ca27eae45 100644
--- a/tests/ui/return/return-from-residual-sugg-issue-125997.rs
+++ b/tests/ui/return/return-from-residual-sugg-issue-125997.rs
@@ -27,6 +27,21 @@ macro_rules! mac {
     };
 }
 
+struct A;
+
+impl A {
+    fn test4(&self) {
+        let mut _file = File::create("foo.txt")?;
+        //~^ ERROR the `?` operator can only be used in a method
+    }
+
+    fn test5(&self) {
+        let mut _file = File::create("foo.txt")?;
+        //~^ ERROR the `?` operator can only be used in a method
+        println!();
+    }
+}
+
 fn main() {
     let mut _file = File::create("foo.txt")?;
     //~^ ERROR the `?` operator can only be used in a function
diff --git a/tests/ui/return/return-from-residual-sugg-issue-125997.stderr b/tests/ui/return/return-from-residual-sugg-issue-125997.stderr
index ef938f0213d..a59f38c2ec6 100644
--- a/tests/ui/return/return-from-residual-sugg-issue-125997.stderr
+++ b/tests/ui/return/return-from-residual-sugg-issue-125997.stderr
@@ -37,8 +37,47 @@ LL +     Ok(())
 LL + }
    |
 
+error[E0277]: the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `FromResidual`)
+  --> $DIR/return-from-residual-sugg-issue-125997.rs:34:48
+   |
+LL |     fn test4(&self) {
+   |     --------------- this function should return `Result` or `Option` to accept `?`
+LL |         let mut _file = File::create("foo.txt")?;
+   |                                                ^ cannot use the `?` operator in a method that returns `()`
+   |
+   = help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
+help: consider adding return type
+   |
+LL ~     fn test4(&self) -> Result<(), Box<dyn std::error::Error>> {
+LL |         let mut _file = File::create("foo.txt")?;
+LL |
+LL ~     
+LL +     Ok(())
+LL + }
+   |
+
+error[E0277]: the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `FromResidual`)
+  --> $DIR/return-from-residual-sugg-issue-125997.rs:39:48
+   |
+LL |     fn test5(&self) {
+   |     --------------- this function should return `Result` or `Option` to accept `?`
+LL |         let mut _file = File::create("foo.txt")?;
+   |                                                ^ cannot use the `?` operator in a method that returns `()`
+   |
+   = help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
+help: consider adding return type
+   |
+LL ~     fn test5(&self) -> Result<(), Box<dyn std::error::Error>> {
+LL |         let mut _file = File::create("foo.txt")?;
+LL |
+LL |         println!();
+LL ~     
+LL +     Ok(())
+LL + }
+   |
+
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
-  --> $DIR/return-from-residual-sugg-issue-125997.rs:31:44
+  --> $DIR/return-from-residual-sugg-issue-125997.rs:46:44
    |
 LL | fn main() {
    | --------- this function should return `Result` or `Option` to accept `?`
@@ -81,6 +120,6 @@ LL +     Ok(())
 LL + }
    |
 
-error: aborting due to 4 previous errors
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0277`.