From 71db0dd918424accf32104d8b780079105a7ac30 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 30 Sep 2022 16:44:39 +0100 Subject: [PATCH] Fix format_args capture for macro expanded format strings --- compiler/rustc_builtin_macros/src/format.rs | 6 +-- .../fmt/auxiliary/format-string-proc-macro.rs | 28 ++++++++++++++ .../fmt/format-args-capture-macro-hygiene.rs | 18 +++++++++ .../format-args-capture-macro-hygiene.stderr | 37 +++++++++++++++++-- src/test/ui/fmt/format-concat-span.stderr | 11 ------ ...ncat-span.rs => format-expanded-string.rs} | 9 +++++ src/test/ui/fmt/format-expanded-string.stderr | 19 ++++++++++ 7 files changed, 111 insertions(+), 17 deletions(-) create mode 100644 src/test/ui/fmt/auxiliary/format-string-proc-macro.rs delete mode 100644 src/test/ui/fmt/format-concat-span.stderr rename src/test/ui/fmt/{format-concat-span.rs => format-expanded-string.rs} (70%) create mode 100644 src/test/ui/fmt/format-expanded-string.stderr diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index b15e2d084ef7f..8b07c11066357 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -159,7 +159,7 @@ pub fn make_format_args( append_newline: bool, ) -> Result { 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, diff --git a/src/test/ui/fmt/auxiliary/format-string-proc-macro.rs b/src/test/ui/fmt/auxiliary/format-string-proc-macro.rs new file mode 100644 index 0000000000000..e44a84776bc69 --- /dev/null +++ b/src/test/ui/fmt/auxiliary/format-string-proc-macro.rs @@ -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)) +} diff --git a/src/test/ui/fmt/format-args-capture-macro-hygiene.rs b/src/test/ui/fmt/format-args-capture-macro-hygiene.rs index fdbd93836ef9f..b04f80ba4061b 100644 --- a/src/test/ui/fmt/format-args-capture-macro-hygiene.rs +++ b/src/test/ui/fmt/format-args-capture-macro-hygiene.rs @@ -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` } diff --git a/src/test/ui/fmt/format-args-capture-macro-hygiene.stderr b/src/test/ui/fmt/format-args-capture-macro-hygiene.stderr index 9423e8c819d7a..1b5fbd2af34d9 100644 --- a/src/test/ui/fmt/format-args-capture-macro-hygiene.stderr +++ b/src/test/ui/fmt/format-args-capture-macro-hygiene.stderr @@ -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 diff --git a/src/test/ui/fmt/format-concat-span.stderr b/src/test/ui/fmt/format-concat-span.stderr deleted file mode 100644 index da46f40abcb97..0000000000000 --- a/src/test/ui/fmt/format-concat-span.stderr +++ /dev/null @@ -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 - diff --git a/src/test/ui/fmt/format-concat-span.rs b/src/test/ui/fmt/format-expanded-string.rs similarity index 70% rename from src/test/ui/fmt/format-concat-span.rs rename to src/test/ui/fmt/format-expanded-string.rs index ce92df0ad92bd..4c716f08c718f 100644 --- a/src/test/ui/fmt/format-concat-span.rs +++ b/src/test/ui/fmt/format-expanded-string.rs @@ -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 } diff --git a/src/test/ui/fmt/format-expanded-string.stderr b/src/test/ui/fmt/format-expanded-string.stderr new file mode 100644 index 0000000000000..26ce7f26958f4 --- /dev/null +++ b/src/test/ui/fmt/format-expanded-string.stderr @@ -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 +