about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs113
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs4
-rw-r--r--tests/crashes/126385.rs10
-rw-r--r--tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.rs14
-rw-r--r--tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.stderr34
-rw-r--r--tests/ui/closures/wrong-closure-arg-suggestion-125325.rs29
-rw-r--r--tests/ui/closures/wrong-closure-arg-suggestion-125325.stderr28
7 files changed, 175 insertions, 57 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index df1a1411cf5..238e4a992a9 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -937,56 +937,81 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         let node = self.infcx.tcx.hir_node(fn_call_id);
         let def_id = hir.enclosing_body_owner(fn_call_id);
         let mut look_at_return = true;
-        // If we can detect the expression to be an `fn` call where the closure was an argument,
-        // we point at the `fn` definition argument...
-        if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(func, args), .. }) = node {
-            let arg_pos = args
+
+        // If the HIR node is a function or method call gets the def ID
+        // of the called function or method and the span and args of the call expr
+        let get_call_details = || {
+            let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else {
+                return None;
+            };
+
+            let typeck_results = self.infcx.tcx.typeck(def_id);
+
+            match kind {
+                hir::ExprKind::Call(expr, args) => {
+                    if let Some(ty::FnDef(def_id, _)) =
+                        typeck_results.node_type_opt(expr.hir_id).as_ref().map(|ty| ty.kind())
+                    {
+                        Some((*def_id, expr.span, *args))
+                    } else {
+                        None
+                    }
+                }
+                hir::ExprKind::MethodCall(_, _, args, span) => {
+                    if let Some(def_id) = typeck_results.type_dependent_def_id(*hir_id) {
+                        Some((def_id, *span, *args))
+                    } else {
+                        None
+                    }
+                }
+                _ => None,
+            }
+        };
+
+        // If we can detect the expression to be an function or method call where the closure was an argument,
+        // we point at the function or method definition argument...
+        if let Some((callee_def_id, call_span, call_args)) = get_call_details() {
+            let arg_pos = call_args
                 .iter()
                 .enumerate()
                 .filter(|(_, arg)| arg.hir_id == closure_id)
                 .map(|(pos, _)| pos)
                 .next();
-            let tables = self.infcx.tcx.typeck(def_id);
-            if let Some(ty::FnDef(def_id, _)) =
-                tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind())
-            {
-                let arg = match hir.get_if_local(*def_id) {
-                    Some(
-                        hir::Node::Item(hir::Item {
-                            ident, kind: hir::ItemKind::Fn(sig, ..), ..
-                        })
-                        | hir::Node::TraitItem(hir::TraitItem {
-                            ident,
-                            kind: hir::TraitItemKind::Fn(sig, _),
-                            ..
+
+            let arg = match hir.get_if_local(callee_def_id) {
+                Some(
+                    hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. })
+                    | hir::Node::TraitItem(hir::TraitItem {
+                        ident,
+                        kind: hir::TraitItemKind::Fn(sig, _),
+                        ..
+                    })
+                    | hir::Node::ImplItem(hir::ImplItem {
+                        ident,
+                        kind: hir::ImplItemKind::Fn(sig, _),
+                        ..
+                    }),
+                ) => Some(
+                    arg_pos
+                        .and_then(|pos| {
+                            sig.decl.inputs.get(
+                                pos + if sig.decl.implicit_self.has_implicit_self() {
+                                    1
+                                } else {
+                                    0
+                                },
+                            )
                         })
-                        | hir::Node::ImplItem(hir::ImplItem {
-                            ident,
-                            kind: hir::ImplItemKind::Fn(sig, _),
-                            ..
-                        }),
-                    ) => Some(
-                        arg_pos
-                            .and_then(|pos| {
-                                sig.decl.inputs.get(
-                                    pos + if sig.decl.implicit_self.has_implicit_self() {
-                                        1
-                                    } else {
-                                        0
-                                    },
-                                )
-                            })
-                            .map(|arg| arg.span)
-                            .unwrap_or(ident.span),
-                    ),
-                    _ => None,
-                };
-                if let Some(span) = arg {
-                    err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
-                    err.span_label(func.span, "expects `Fn` instead of `FnMut`");
-                    err.span_label(closure_span, "in this closure");
-                    look_at_return = false;
-                }
+                        .map(|arg| arg.span)
+                        .unwrap_or(ident.span),
+                ),
+                _ => None,
+            };
+            if let Some(span) = arg {
+                err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
+                err.span_label(call_span, "expects `Fn` instead of `FnMut`");
+                err.span_label(closure_span, "in this closure");
+                look_at_return = false;
             }
         }
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 2cf548e28b1..77fb9fb4315 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -628,9 +628,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
                     | GenericArgKind::Const(_),
                     _,
                 ) => {
-                    // This was previously a `span_delayed_bug` and could be
-                    // reached by the test for #82126, but no longer.
-                    self.dcx().span_bug(
+                    self.dcx().span_delayed_bug(
                         hir_arg.span(),
                         format!("unmatched arg and hir arg: found {kind:?} vs {hir_arg:?}"),
                     );
diff --git a/tests/crashes/126385.rs b/tests/crashes/126385.rs
deleted file mode 100644
index 9e74e88c1ff..00000000000
--- a/tests/crashes/126385.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-//@ known-bug: rust-lang/rust#126385
-pub struct MyStruct<'field> {
-    field: &'_ [u32],
-}
-
-impl MyStruct<'_> {
-    pub fn _<'a>(field: &'a[u32]) -> Self<new> {
-    Self{field}
-    }
-}
diff --git a/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.rs b/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.rs
new file mode 100644
index 00000000000..637c47f2939
--- /dev/null
+++ b/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.rs
@@ -0,0 +1,14 @@
+// This test was triggering a `span_bug` crash, which was then fixed by
+// downgrading it to a `span_delayed_bug`.
+
+pub struct MyStruct<'field> {
+    field: &'field [u32],
+}
+
+impl MyStruct<'_> {
+    pub fn f(field: &[u32]) -> Self<u32> { //~ ERROR type arguments are not allowed on self type
+        Self { field }                     //~ ERROR lifetime may not live long enough
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.stderr b/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.stderr
new file mode 100644
index 00000000000..0ae301b2090
--- /dev/null
+++ b/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.stderr
@@ -0,0 +1,34 @@
+error[E0109]: type arguments are not allowed on self type
+  --> $DIR/unmatched-arg-and-hir-arg-issue-126385.rs:9:37
+   |
+LL |     pub fn f(field: &[u32]) -> Self<u32> {
+   |                                ---- ^^^ type argument not allowed
+   |                                |
+   |                                not allowed on self type
+   |
+note: `Self` is of type `MyStruct<'_>`
+  --> $DIR/unmatched-arg-and-hir-arg-issue-126385.rs:4:12
+   |
+LL | pub struct MyStruct<'field> {
+   |            ^^^^^^^^ `Self` corresponds to this type
+...
+LL | impl MyStruct<'_> {
+   | ----------------- `Self` is on type `MyStruct` in this `impl`
+help: the `Self` type doesn't accept type parameters, use the concrete type's name `MyStruct` instead if you want to specify its type parameters
+   |
+LL |     pub fn f(field: &[u32]) -> MyStruct<u32> {
+   |                                ~~~~~~~~
+
+error: lifetime may not live long enough
+  --> $DIR/unmatched-arg-and-hir-arg-issue-126385.rs:10:9
+   |
+LL |     pub fn f(field: &[u32]) -> Self<u32> {
+   |                     -          --------- return type is MyStruct<'2>
+   |                     |
+   |                     let's call the lifetime of this reference `'1`
+LL |         Self { field }
+   |         ^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0109`.
diff --git a/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs b/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs
new file mode 100644
index 00000000000..ce575697cf6
--- /dev/null
+++ b/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs
@@ -0,0 +1,29 @@
+// Regression test for #125325
+
+// Tests that we suggest changing an `impl Fn` param
+// to `impl FnMut` when the provided closure arg
+// is trying to mutate the closure env.
+// Ensures that it works that way for both
+// functions and methods
+
+struct S;
+
+impl S {
+    fn assoc_func(&self, _f: impl Fn()) -> usize {
+        0
+    }
+}
+
+fn func(_f: impl Fn()) -> usize {
+    0
+}
+
+fn test_func(s: &S) -> usize {
+    let mut x = ();
+    s.assoc_func(|| x = ());
+    //~^ cannot assign to `x`, as it is a captured variable in a `Fn` closure
+    func(|| x = ())
+    //~^ cannot assign to `x`, as it is a captured variable in a `Fn` closure
+}
+
+fn main() {}
diff --git a/tests/ui/closures/wrong-closure-arg-suggestion-125325.stderr b/tests/ui/closures/wrong-closure-arg-suggestion-125325.stderr
new file mode 100644
index 00000000000..e0cce8c4b31
--- /dev/null
+++ b/tests/ui/closures/wrong-closure-arg-suggestion-125325.stderr
@@ -0,0 +1,28 @@
+error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure
+  --> $DIR/wrong-closure-arg-suggestion-125325.rs:23:21
+   |
+LL |     fn assoc_func(&self, _f: impl Fn()) -> usize {
+   |                              --------- change this to accept `FnMut` instead of `Fn`
+...
+LL |     s.assoc_func(|| x = ());
+   |       --------------^^^^^^-
+   |       |          |  |
+   |       |          |  cannot assign
+   |       |          in this closure
+   |       expects `Fn` instead of `FnMut`
+
+error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure
+  --> $DIR/wrong-closure-arg-suggestion-125325.rs:25:13
+   |
+LL | fn func(_f: impl Fn()) -> usize {
+   |             --------- change this to accept `FnMut` instead of `Fn`
+...
+LL |     func(|| x = ())
+   |     ---- -- ^^^^^^ cannot assign
+   |     |    |
+   |     |    in this closure
+   |     expects `Fn` instead of `FnMut`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0594`.