Skip to content

Commit 711f6b6

Browse files
committed
Fix suggestions span for when expr is from macro expansions
1 parent 089677e commit 711f6b6

5 files changed

+193
-4
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -907,18 +907,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
907907
};
908908
let ty = self.normalize(expr.span, ty);
909909
if self.can_coerce(found, ty) {
910+
let span = self.find_suggestable_ancestor_span(expr);
911+
910912
err.multipart_suggestion(
911913
"you might have meant to return this value",
912914
vec![
913-
(expr.span.shrink_to_lo(), "return ".to_string()),
914-
(expr.span.shrink_to_hi(), ";".to_string()),
915+
(span.shrink_to_lo(), "return ".to_string()),
916+
(span.shrink_to_hi(), ";".to_string()),
915917
],
916918
Applicability::MaybeIncorrect,
917919
);
918920
}
919921
}
920922
}
921923

924+
/// Given an expression (possibly as a product of multiple macro expansions in a chain), find
925+
/// the closest ancestor span which is no longer the source of a macro expansion.
926+
fn find_suggestable_ancestor_span(&self, expr: &Expr<'_>) -> Span {
927+
let mut node_id = self.tcx.hir().parent_id(expr.hir_id);
928+
for (id, _) in self.tcx.hir().parent_iter(node_id) {
929+
if self.tcx.hir().span(id).can_be_used_for_suggestions() {
930+
node_id = id;
931+
break;
932+
}
933+
}
934+
let parent = self.tcx.hir().get_enclosing_scope(node_id).unwrap_or(node_id);
935+
let parent_span = self.tcx.hir().span(parent);
936+
let span = expr.span.find_ancestor_inside(parent_span).unwrap_or(expr.span);
937+
span
938+
}
939+
922940
pub(in super::super) fn suggest_missing_parentheses(
923941
&self,
924942
err: &mut Diagnostic,
@@ -1102,10 +1120,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11021120
),
11031121
))
11041122
{
1123+
let span = self.find_suggestable_ancestor_span(expr);
1124+
11051125
let sugg = if expr.precedence().order() >= PREC_POSTFIX {
1106-
vec![(expr.span.shrink_to_hi(), ".into()".to_owned())]
1126+
vec![(span.shrink_to_hi(), ".into()".to_owned())]
11071127
} else {
1108-
vec![(expr.span.shrink_to_lo(), "(".to_owned()), (expr.span.shrink_to_hi(), ").into()".to_owned())]
1128+
vec![(span.shrink_to_lo(), "(".to_owned()), (span.shrink_to_hi(), ").into()".to_owned())]
11091129
};
11101130
diag.multipart_suggestion(
11111131
format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
pub fn foo(x: &str) -> Result<(), Box<dyn std::error::Error>> {
2+
Err(format!("error: {x}"))
3+
//~^ ERROR mismatched types
4+
}
5+
6+
macro_rules! outer {
7+
($x: expr) => {
8+
inner!($x)
9+
}
10+
}
11+
12+
macro_rules! inner {
13+
($x: expr) => {
14+
format!("error: {}", $x)
15+
//~^ ERROR mismatched types
16+
}
17+
}
18+
19+
fn bar(x: &str) -> Result<(), Box<dyn std::error::Error>> {
20+
Err(outer!(x))
21+
}
22+
23+
macro_rules! entire_fn_outer {
24+
() => {
25+
entire_fn!();
26+
}
27+
}
28+
29+
macro_rules! entire_fn {
30+
() => {
31+
pub fn baz(x: &str) -> Result<(), Box<dyn std::error::Error>> {
32+
Err(format!("error: {x}"))
33+
//~^ ERROR mismatched types
34+
}
35+
}
36+
}
37+
38+
entire_fn_outer!();
39+
40+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-110017-format-into-help-deletes-macro.rs:2:9
3+
|
4+
LL | Err(format!("error: {x}"))
5+
| ^^^^^^^^^^^^^^^^^^^^^ expected `Box<dyn Error>`, found `String`
6+
|
7+
= note: expected struct `Box<dyn std::error::Error>`
8+
found struct `String`
9+
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
10+
help: call `Into::into` on this expression to convert `String` into `Box<dyn std::error::Error>`
11+
|
12+
LL | Err(format!("error: {x}").into())
13+
| +++++++
14+
15+
error[E0308]: mismatched types
16+
--> $DIR/issue-110017-format-into-help-deletes-macro.rs:20:9
17+
|
18+
LL | Err(outer!(x))
19+
| ^^^^^^^^^ expected `Box<dyn Error>`, found `String`
20+
|
21+
= note: expected struct `Box<dyn std::error::Error>`
22+
found struct `String`
23+
= 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)
24+
help: call `Into::into` on this expression to convert `String` into `Box<dyn std::error::Error>`
25+
|
26+
LL | Err(outer!(x).into())
27+
| +++++++
28+
29+
error[E0308]: mismatched types
30+
--> $DIR/issue-110017-format-into-help-deletes-macro.rs:38:1
31+
|
32+
LL | entire_fn_outer!();
33+
| ^^^^^^^^^^^^^^^^^^ expected `Box<dyn Error>`, found `String`
34+
|
35+
= note: expected struct `Box<dyn std::error::Error>`
36+
found struct `String`
37+
= 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)
38+
help: call `Into::into` on this expression to convert `String` into `Box<dyn std::error::Error>`
39+
|
40+
LL | Err(format!("error: {x}").into())
41+
| +++++++
42+
43+
error: aborting due to 3 previous errors
44+
45+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// https://github.com/rust-lang/rust/issues/112007
2+
fn bug_report<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result {
3+
if true {
4+
writeln!(w, "`;?` here ->")?;
5+
} else {
6+
writeln!(w, "but not here")
7+
//~^ ERROR mismatched types
8+
}
9+
Ok(())
10+
}
11+
12+
macro_rules! baz {
13+
($w: expr) => {
14+
bar!($w)
15+
}
16+
}
17+
18+
macro_rules! bar {
19+
($w: expr) => {
20+
writeln!($w, "but not here")
21+
//~^ ERROR mismatched types
22+
}
23+
}
24+
25+
fn foo<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result {
26+
if true {
27+
writeln!(w, "`;?` here ->")?;
28+
} else {
29+
baz!(w)
30+
}
31+
Ok(())
32+
}
33+
34+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-112007-leaked-writeln-macro-internals.rs:6:9
3+
|
4+
LL | / if true {
5+
LL | | writeln!(w, "`;?` here ->")?;
6+
LL | | } else {
7+
LL | | writeln!(w, "but not here")
8+
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `Result<(), Error>`
9+
LL | |
10+
LL | | }
11+
| |_____- expected this to be `()`
12+
|
13+
= note: expected unit type `()`
14+
found enum `Result<(), std::fmt::Error>`
15+
= note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info)
16+
help: consider using a semicolon here
17+
|
18+
LL | };
19+
| +
20+
help: you might have meant to return this value
21+
|
22+
LL | return writeln!(w, "but not here");
23+
| ++++++ +
24+
25+
error[E0308]: mismatched types
26+
--> $DIR/issue-112007-leaked-writeln-macro-internals.rs:29:9
27+
|
28+
LL | / if true {
29+
LL | | writeln!(w, "`;?` here ->")?;
30+
LL | | } else {
31+
LL | | baz!(w)
32+
| | ^^^^^^^ expected `()`, found `Result<(), Error>`
33+
LL | | }
34+
| |_____- expected this to be `()`
35+
|
36+
= note: expected unit type `()`
37+
found enum `Result<(), std::fmt::Error>`
38+
= 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)
39+
help: consider using a semicolon here
40+
|
41+
LL | };
42+
| +
43+
help: you might have meant to return this value
44+
|
45+
LL | return baz!(w);
46+
| ++++++ +
47+
48+
error: aborting due to 2 previous errors
49+
50+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)