Skip to content

Fix format_args capture for macro expanded format strings #102519

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions compiler/rustc_builtin_macros/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ pub fn make_format_args(
append_newline: bool,
) -> Result<FormatArgs, ()> {
let msg = "format argument must be a string literal";
let fmt_span = efmt.span;
let unexpanded_fmt_span = efmt.span;
let (fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string(ecx, efmt, msg) {
Ok(mut fmt) if append_newline => {
fmt.0 = Symbol::intern(&format!("{}\n", fmt.0));
Expand All @@ -174,7 +174,7 @@ pub fn make_format_args(
};
if !suggested {
err.span_suggestion(
fmt_span.shrink_to_lo(),
unexpanded_fmt_span.shrink_to_lo(),
"you might be missing a string literal to format with",
format!("\"{}\", ", sugg_fmt),
Applicability::MaybeIncorrect,
Expand All @@ -192,7 +192,7 @@ pub fn make_format_args(
};

let fmt_str = fmt_str.as_str(); // for the suggestions below
let fmt_snippet = ecx.source_map().span_to_snippet(fmt_span).ok();
let fmt_snippet = ecx.source_map().span_to_snippet(unexpanded_fmt_span).ok();
let mut parser = parse::Parser::new(
fmt_str,
str_style,
Expand Down
28 changes: 28 additions & 0 deletions src/test/ui/fmt/auxiliary/format-string-proc-macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// force-host
// no-prefer-dynamic

#![crate_type = "proc-macro"]

extern crate proc_macro;

use proc_macro::{Literal, Span, TokenStream, TokenTree};

#[proc_macro]
pub fn foo_with_input_span(input: TokenStream) -> TokenStream {
let span = input.into_iter().next().unwrap().span();

let mut lit = Literal::string("{foo}");
lit.set_span(span);

TokenStream::from(TokenTree::Literal(lit))
}

#[proc_macro]
pub fn err_with_input_span(input: TokenStream) -> TokenStream {
let span = input.into_iter().next().unwrap().span();

let mut lit = Literal::string(" }");
lit.set_span(span);

TokenStream::from(TokenTree::Literal(lit))
}
18 changes: 18 additions & 0 deletions src/test/ui/fmt/format-args-capture-macro-hygiene.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
// aux-build:format-string-proc-macro.rs

#[macro_use]
extern crate format_string_proc_macro;

macro_rules! def_site {
() => { "{foo}" } //~ ERROR: there is no argument named `foo`
}

macro_rules! call_site {
($fmt:literal) => { $fmt }
}

fn main() {
format!(concat!("{foo}")); //~ ERROR: there is no argument named `foo`
format!(concat!("{ba", "r} {}"), 1); //~ ERROR: there is no argument named `bar`

format!(def_site!());
format!(call_site!("{foo}")); //~ ERROR: there is no argument named `foo`

format!(foo_with_input_span!("")); //~ ERROR: there is no argument named `foo`
}
37 changes: 34 additions & 3 deletions src/test/ui/fmt/format-args-capture-macro-hygiene.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: there is no argument named `foo`
--> $DIR/format-args-capture-macro-hygiene.rs:2:13
--> $DIR/format-args-capture-macro-hygiene.rs:15:13
|
LL | format!(concat!("{foo}"));
| ^^^^^^^^^^^^^^^^
Expand All @@ -9,7 +9,7 @@ LL | format!(concat!("{foo}"));
= note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info)

error: there is no argument named `bar`
--> $DIR/format-args-capture-macro-hygiene.rs:3:13
--> $DIR/format-args-capture-macro-hygiene.rs:16:13
|
LL | format!(concat!("{ba", "r} {}"), 1);
| ^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -18,5 +18,36 @@ LL | format!(concat!("{ba", "r} {}"), 1);
= note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro
= note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 2 previous errors
error: there is no argument named `foo`
--> $DIR/format-args-capture-macro-hygiene.rs:7:13
|
LL | () => { "{foo}" }
| ^^^^^^^
...
LL | format!(def_site!());
| ----------- in this macro invocation
|
= note: did you intend to capture a variable `foo` from the surrounding scope?
= note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro
= note: this error originates in the macro `def_site` (in Nightly builds, run with -Z macro-backtrace for more info)

error: there is no argument named `foo`
--> $DIR/format-args-capture-macro-hygiene.rs:19:24
|
LL | format!(call_site!("{foo}"));
| ^^^^^^^
|
= note: did you intend to capture a variable `foo` from the surrounding scope?
= note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro

error: there is no argument named `foo`
--> $DIR/format-args-capture-macro-hygiene.rs:21:34
|
LL | format!(foo_with_input_span!(""));
| ^^
|
= note: did you intend to capture a variable `foo` from the surrounding scope?
= note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro

error: aborting due to 5 previous errors

11 changes: 0 additions & 11 deletions src/test/ui/fmt/format-concat-span.stderr

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// aux-build:format-string-proc-macro.rs

#[macro_use]
extern crate format_string_proc_macro;


// If the format string is another macro invocation, rustc would previously
// compute nonsensical spans, such as:
//
Expand All @@ -12,4 +18,7 @@
fn main() {
format!(concat!("abc}"));
//~^ ERROR: invalid format string: unmatched `}` found

format!(err_with_input_span!(""));
//~^ ERROR: invalid format string: unmatched `}` found
}
19 changes: 19 additions & 0 deletions src/test/ui/fmt/format-expanded-string.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error: invalid format string: unmatched `}` found
--> $DIR/format-expanded-string.rs:19:13
|
LL | format!(concat!("abc}"));
| ^^^^^^^^^^^^^^^ unmatched `}` in format string
|
= note: if you intended to print `}`, you can escape it using `}}`
= note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info)

error: invalid format string: unmatched `}` found
--> $DIR/format-expanded-string.rs:22:34
|
LL | format!(err_with_input_span!(""));
| ^^ unmatched `}` in format string
|
= note: if you intended to print `}`, you can escape it using `}}`

error: aborting due to 2 previous errors