Skip to content

Commit

Permalink
Fix suggestions span for when expr is from macro expansions
Browse files Browse the repository at this point in the history
  • Loading branch information
jieyouxu committed May 29, 2023
1 parent 089677e commit 711f6b6
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 4 deletions.
28 changes: 24 additions & 4 deletions compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -907,18 +907,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
let ty = self.normalize(expr.span, ty);
if self.can_coerce(found, ty) {
let span = self.find_suggestable_ancestor_span(expr);

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()),
(span.shrink_to_lo(), "return ".to_string()),
(span.shrink_to_hi(), ";".to_string()),
],
Applicability::MaybeIncorrect,
);
}
}
}

/// Given an expression (possibly as a product of multiple macro expansions in a chain), find
/// the closest ancestor span which is no longer the source of a macro expansion.
fn find_suggestable_ancestor_span(&self, expr: &Expr<'_>) -> Span {
let mut node_id = self.tcx.hir().parent_id(expr.hir_id);
for (id, _) in self.tcx.hir().parent_iter(node_id) {
if self.tcx.hir().span(id).can_be_used_for_suggestions() {
node_id = id;
break;
}
}
let parent = self.tcx.hir().get_enclosing_scope(node_id).unwrap_or(node_id);
let parent_span = self.tcx.hir().span(parent);
let span = expr.span.find_ancestor_inside(parent_span).unwrap_or(expr.span);
span
}

pub(in super::super) fn suggest_missing_parentheses(
&self,
err: &mut Diagnostic,
Expand Down Expand Up @@ -1102,10 +1120,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
),
))
{
let span = self.find_suggestable_ancestor_span(expr);

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}`"),
Expand Down
40 changes: 40 additions & 0 deletions tests/ui/typeck/issue-110017-format-into-help-deletes-macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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!();

fn main() {}
45 changes: 45 additions & 0 deletions tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
error[E0308]: mismatched types
--> $DIR/issue-110017-format-into-help-deletes-macro.rs:2:9
|
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:20:9
|
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 | Err(outer!(x).into())
| +++++++

error[E0308]: mismatched types
--> $DIR/issue-110017-format-into-help-deletes-macro.rs:38:1
|
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: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0308`.
34 changes: 34 additions & 0 deletions tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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() {}
50 changes: 50 additions & 0 deletions tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
error[E0308]: mismatched types
--> $DIR/issue-112007-leaked-writeln-macro-internals.rs:6: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:29: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`.

0 comments on commit 711f6b6

Please sign in to comment.