Fix format_args capture for macro expanded format strings

This commit is contained in:
Alex Macleod 2022-09-30 16:44:39 +01:00
parent f914b82a75
commit 71db0dd918
7 changed files with 111 additions and 17 deletions

View File

@ -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));
@ -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,
@ -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,

View File

@ -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))
}

View File

@ -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`
}

View File

@ -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}"));
| ^^^^^^^^^^^^^^^^
@ -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);
| ^^^^^^^^^^^^^^^^^^^^^^^
@ -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

View File

@ -1,11 +0,0 @@
error: invalid format string: unmatched `}` found
--> $DIR/format-concat-span.rs:13: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: aborting due to previous error

View File

@ -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:
//
@ -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
}

View File

@ -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