about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-10-01 14:15:29 +0000
committerbors <bors@rust-lang.org>2022-10-01 14:15:29 +0000
commitedadc7ccdda644ef8149869d2f24018a1dac202a (patch)
treef8f2788724ae71262ba2c85c6c1625dc3f0cfa36
parent744e397d8855f7da87d70aa8d0bd9e0f5f0b51a1 (diff)
parent71db0dd918424accf32104d8b780079105a7ac30 (diff)
downloadrust-edadc7ccdda644ef8149869d2f24018a1dac202a.tar.gz
rust-edadc7ccdda644ef8149869d2f24018a1dac202a.zip
Auto merge of #102519 - Alexendoo:format-args-macro-str, r=m-ou-se
Fix `format_args` capture for macro expanded format strings

Since #100996 `format_args` capture for macro expanded strings aren't prevented when the span of the expansion points to a string literal, e.g.

```rust
// not a terribly realistic example, but also happens for proc_macros that set
// the span of the output to an input str literal, such as indoc
macro_rules! x {
    ($e:expr) => { $e }
}

fn main() {
    let a = 1;
    println!(x!("{a}"));
}
```

The tests didn't catch it as the span of `concat!()` points to the macro invocation

r? `@m-ou-se`
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs6
-rw-r--r--src/test/ui/fmt/auxiliary/format-string-proc-macro.rs28
-rw-r--r--src/test/ui/fmt/format-args-capture-macro-hygiene.rs18
-rw-r--r--src/test/ui/fmt/format-args-capture-macro-hygiene.stderr37
-rw-r--r--src/test/ui/fmt/format-concat-span.stderr11
-rw-r--r--src/test/ui/fmt/format-expanded-string.rs (renamed from src/test/ui/fmt/format-concat-span.rs)9
-rw-r--r--src/test/ui/fmt/format-expanded-string.stderr19
7 files changed, 111 insertions, 17 deletions
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index b15e2d084ef..8b07c110663 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<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,
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 00000000000..e44a84776bc
--- /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 fdbd93836ef..b04f80ba406 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 9423e8c819d..1b5fbd2af34 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 da46f40abcb..00000000000
--- 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
index ce92df0ad92..4c716f08c71 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 00000000000..26ce7f26958
--- /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
+