about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-08-03 10:01:16 +0000
committerbors <bors@rust-lang.org>2023-08-03 10:01:16 +0000
commitc115ec11d2087050dc12c5c83959979aa98bb3e5 (patch)
tree5b96ab0c4fab46b9217cd88f8bdecae48838a9cb
parenta922d1c0da0c904ffe9de256d926180613f1ed81 (diff)
parent25d065b2d476053102b05bbd5c4650a86ddc2c94 (diff)
downloadrust-c115ec11d2087050dc12c5c83959979aa98bb3e5.tar.gz
rust-c115ec11d2087050dc12c5c83959979aa98bb3e5.zip
Auto merge of #112043 - jieyouxu:suggestion_macro_expansion_source_callsites, r=cjgillot
Fix suggestion spans for expr from macro expansions

### Issue #112007: rustc shows expanded `writeln!` macro in code suggestion

#### Before This PR

```
help: consider using a semicolon here
  |
6 |     };
  |      +
help: you might have meant to return this value
 --> C:\Users\hayle\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\macros\mod.rs:557:9
  |
55|         return $dst.write_fmt($crate::format_args_nl!($($arg)*));
  |         ++++++                                                  +
```

#### After This PR

```
help: consider using a semicolon here
   |
LL |     };
   |      +
help: you might have meant to return this value
   |
LL |         return writeln!(w, "but not here");
   |         ++++++                            +
```

### Issue #110017: `format!` `.into()` suggestion deletes the `format` macro

#### Before This PR

```
help: call `Into::into` on this expression to convert `String` into `Box<dyn std::error::Error>`
 --> /Users/eric/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/macros.rs:121:12
  |
12|         res.into()
  |            +++++++
```

#### After This PR

```
help: call `Into::into` on this expression to convert `String` into `Box<dyn std::error::Error>`
   |
LL |     Err(format!("error: {x}").into())
   |                              +++++++
```

---

Fixes #112007.
Fixes #110017.
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs31
-rw-r--r--tests/ui/typeck/issue-110017-format-into-help-deletes-macro.fixed55
-rw-r--r--tests/ui/typeck/issue-110017-format-into-help-deletes-macro.rs55
-rw-r--r--tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr59
-rw-r--r--tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.fixed37
-rw-r--r--tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.rs37
-rw-r--r--tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr50
7 files changed, 314 insertions, 10 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index c49e4381897..d4edd08d302 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -994,14 +994,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             };
             let ty = self.normalize(expr.span, ty);
             if self.can_coerce(found, ty) {
-                err.multipart_suggestion(
-                    "you might have meant to return this value",
-                    vec![
-                        (expr.span.shrink_to_lo(), "return ".to_string()),
-                        (expr.span.shrink_to_hi(), ";".to_string()),
-                    ],
-                    Applicability::MaybeIncorrect,
-                );
+                if let Some(node) = self.tcx.hir().find(fn_id)
+                    && let Some(owner_node) = node.as_owner()
+                    && let Some(span) = expr.span.find_ancestor_inside(owner_node.span())
+                {
+                    err.multipart_suggestion(
+                        "you might have meant to return this value",
+                        vec![
+                            (span.shrink_to_lo(), "return ".to_string()),
+                            (span.shrink_to_hi(), ";".to_string()),
+                        ],
+                        Applicability::MaybeIncorrect,
+                    );
+                }
             }
         }
     }
@@ -1185,10 +1190,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ),
             ))
         {
+            let mut span = expr.span;
+            while expr.span.eq_ctxt(span) && let Some(parent_callsite) = span.parent_callsite()
+            {
+                span = parent_callsite;
+            }
+
             let sugg = if expr.precedence().order() >= PREC_POSTFIX {
-                vec![(expr.span.shrink_to_hi(), ".into()".to_owned())]
+                vec![(span.shrink_to_hi(), ".into()".to_owned())]
             } else {
-                vec![(expr.span.shrink_to_lo(), "(".to_owned()), (expr.span.shrink_to_hi(), ").into()".to_owned())]
+                vec![(span.shrink_to_lo(), "(".to_owned()), (span.shrink_to_hi(), ").into()".to_owned())]
             };
             diag.multipart_suggestion(
                 format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"),
diff --git a/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.fixed b/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.fixed
new file mode 100644
index 00000000000..b101cf1dcf5
--- /dev/null
+++ b/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.fixed
@@ -0,0 +1,55 @@
+// run-rustfix
+#![allow(dead_code)]
+
+ pub fn foo(x: &str) -> Result<(), Box<dyn std::error::Error>> {
+     Err(format!("error: {x}").into())
+     //~^ ERROR mismatched types
+ }
+
+ macro_rules! outer {
+     ($x: expr) => {
+         inner!($x)
+     }
+ }
+
+ macro_rules! inner {
+     ($x: expr) => {
+         format!("error: {}", $x).into()
+         //~^ ERROR mismatched types
+     }
+ }
+
+ fn bar(x: &str) -> Result<(), Box<dyn std::error::Error>> {
+     Err(outer!(x))
+ }
+
+ macro_rules! entire_fn_outer {
+     () => {
+         entire_fn!();
+     }
+ }
+
+ macro_rules! entire_fn {
+     () => {
+         pub fn baz(x: &str) -> Result<(), Box<dyn std::error::Error>> {
+             Err(format!("error: {x}").into())
+             //~^ ERROR mismatched types
+         }
+     }
+ }
+
+ entire_fn_outer!();
+
+macro_rules! nontrivial {
+    ($x: expr) => {
+        Err(format!("error: {}", $x).into())
+        //~^ ERROR mismatched types
+    }
+}
+
+pub fn qux(x: &str) -> Result<(), Box<dyn std::error::Error>> {
+    nontrivial!(x)
+}
+
+
+fn main() {}
diff --git a/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.rs b/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.rs
new file mode 100644
index 00000000000..cfde912d896
--- /dev/null
+++ b/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.rs
@@ -0,0 +1,55 @@
+// run-rustfix
+#![allow(dead_code)]
+
+ pub fn foo(x: &str) -> Result<(), Box<dyn std::error::Error>> {
+     Err(format!("error: {x}"))
+     //~^ ERROR mismatched types
+ }
+
+ macro_rules! outer {
+     ($x: expr) => {
+         inner!($x)
+     }
+ }
+
+ macro_rules! inner {
+     ($x: expr) => {
+         format!("error: {}", $x)
+         //~^ ERROR mismatched types
+     }
+ }
+
+ fn bar(x: &str) -> Result<(), Box<dyn std::error::Error>> {
+     Err(outer!(x))
+ }
+
+ macro_rules! entire_fn_outer {
+     () => {
+         entire_fn!();
+     }
+ }
+
+ macro_rules! entire_fn {
+     () => {
+         pub fn baz(x: &str) -> Result<(), Box<dyn std::error::Error>> {
+             Err(format!("error: {x}"))
+             //~^ ERROR mismatched types
+         }
+     }
+ }
+
+ entire_fn_outer!();
+
+macro_rules! nontrivial {
+    ($x: expr) => {
+        Err(format!("error: {}", $x))
+        //~^ ERROR mismatched types
+    }
+}
+
+pub fn qux(x: &str) -> Result<(), Box<dyn std::error::Error>> {
+    nontrivial!(x)
+}
+
+
+fn main() {}
diff --git a/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr b/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr
new file mode 100644
index 00000000000..e4834c0308b
--- /dev/null
+++ b/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr
@@ -0,0 +1,59 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-110017-format-into-help-deletes-macro.rs:5:10
+   |
+LL |      Err(format!("error: {x}"))
+   |          ^^^^^^^^^^^^^^^^^^^^^ expected `Box<dyn Error>`, found `String`
+   |
+   = note: expected struct `Box<dyn std::error::Error>`
+              found struct `String`
+   = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: call `Into::into` on this expression to convert `String` into `Box<dyn std::error::Error>`
+   |
+LL |      Err(format!("error: {x}").into())
+   |                               +++++++
+
+error[E0308]: mismatched types
+  --> $DIR/issue-110017-format-into-help-deletes-macro.rs:23:10
+   |
+LL |      Err(outer!(x))
+   |          ^^^^^^^^^ expected `Box<dyn Error>`, found `String`
+   |
+   = note: expected struct `Box<dyn std::error::Error>`
+              found struct `String`
+   = note: this error originates in the macro `format` which comes from the expansion of the macro `outer` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: call `Into::into` on this expression to convert `String` into `Box<dyn std::error::Error>`
+   |
+LL |          format!("error: {}", $x).into()
+   |                                  +++++++
+
+error[E0308]: mismatched types
+  --> $DIR/issue-110017-format-into-help-deletes-macro.rs:41:2
+   |
+LL |  entire_fn_outer!();
+   |  ^^^^^^^^^^^^^^^^^^ expected `Box<dyn Error>`, found `String`
+   |
+   = note: expected struct `Box<dyn std::error::Error>`
+              found struct `String`
+   = note: this error originates in the macro `format` which comes from the expansion of the macro `entire_fn_outer` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: call `Into::into` on this expression to convert `String` into `Box<dyn std::error::Error>`
+   |
+LL |              Err(format!("error: {x}").into())
+   |                                       +++++++
+
+error[E0308]: mismatched types
+  --> $DIR/issue-110017-format-into-help-deletes-macro.rs:51:5
+   |
+LL |     nontrivial!(x)
+   |     ^^^^^^^^^^^^^^ expected `Box<dyn Error>`, found `String`
+   |
+   = note: expected struct `Box<dyn std::error::Error>`
+              found struct `String`
+   = note: this error originates in the macro `format` which comes from the expansion of the macro `nontrivial` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: call `Into::into` on this expression to convert `String` into `Box<dyn std::error::Error>`
+   |
+LL |         Err(format!("error: {}", $x).into())
+   |                                     +++++++
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.fixed b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.fixed
new file mode 100644
index 00000000000..29b6b8b868f
--- /dev/null
+++ b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.fixed
@@ -0,0 +1,37 @@
+// run-rustfix
+#![allow(dead_code)]
+
+// https://github.com/rust-lang/rust/issues/112007
+fn bug_report<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result {
+    if true {
+        writeln!(w, "`;?` here ->")?;
+    } else {
+        return writeln!(w, "but not here");
+        //~^ ERROR mismatched types
+    };
+    Ok(())
+}
+
+macro_rules! baz {
+    ($w: expr) => {
+        bar!($w)
+    }
+}
+
+macro_rules! bar {
+    ($w: expr) => {
+        writeln!($w, "but not here")
+        //~^ ERROR mismatched types
+    }
+}
+
+fn foo<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result {
+    if true {
+        writeln!(w, "`;?` here ->")?;
+    } else {
+        return baz!(w);
+    };
+    Ok(())
+}
+
+fn main() {}
diff --git a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.rs b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.rs
new file mode 100644
index 00000000000..bd731e02bd1
--- /dev/null
+++ b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.rs
@@ -0,0 +1,37 @@
+// run-rustfix
+#![allow(dead_code)]
+
+// https://github.com/rust-lang/rust/issues/112007
+fn bug_report<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result {
+    if true {
+        writeln!(w, "`;?` here ->")?;
+    } else {
+        writeln!(w, "but not here")
+        //~^ ERROR mismatched types
+    }
+    Ok(())
+}
+
+macro_rules! baz {
+    ($w: expr) => {
+        bar!($w)
+    }
+}
+
+macro_rules! bar {
+    ($w: expr) => {
+        writeln!($w, "but not here")
+        //~^ ERROR mismatched types
+    }
+}
+
+fn foo<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result {
+    if true {
+        writeln!(w, "`;?` here ->")?;
+    } else {
+        baz!(w)
+    }
+    Ok(())
+}
+
+fn main() {}
diff --git a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr
new file mode 100644
index 00000000000..df2e06e8f3b
--- /dev/null
+++ b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr
@@ -0,0 +1,50 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-112007-leaked-writeln-macro-internals.rs:9:9
+   |
+LL | /     if true {
+LL | |         writeln!(w, "`;?` here ->")?;
+LL | |     } else {
+LL | |         writeln!(w, "but not here")
+   | |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `Result<(), Error>`
+LL | |
+LL | |     }
+   | |_____- expected this to be `()`
+   |
+   = note: expected unit type `()`
+                   found enum `Result<(), std::fmt::Error>`
+   = note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider using a semicolon here
+   |
+LL |     };
+   |      +
+help: you might have meant to return this value
+   |
+LL |         return writeln!(w, "but not here");
+   |         ++++++                            +
+
+error[E0308]: mismatched types
+  --> $DIR/issue-112007-leaked-writeln-macro-internals.rs:32:9
+   |
+LL | /     if true {
+LL | |         writeln!(w, "`;?` here ->")?;
+LL | |     } else {
+LL | |         baz!(w)
+   | |         ^^^^^^^ expected `()`, found `Result<(), Error>`
+LL | |     }
+   | |_____- expected this to be `()`
+   |
+   = note: expected unit type `()`
+                   found enum `Result<(), std::fmt::Error>`
+   = note: this error originates in the macro `writeln` which comes from the expansion of the macro `baz` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider using a semicolon here
+   |
+LL |     };
+   |      +
+help: you might have meant to return this value
+   |
+LL |         return baz!(w);
+   |         ++++++        +
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.