Skip to content

Commit edadc7c

Browse files
committed
Auto merge of rust-lang#102519 - Alexendoo:format-args-macro-str, r=m-ou-se
Fix `format_args` capture for macro expanded format strings Since rust-lang#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`
2 parents 744e397 + 71db0dd commit edadc7c

7 files changed

+111
-17
lines changed

compiler/rustc_builtin_macros/src/format.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ pub fn make_format_args(
159159
append_newline: bool,
160160
) -> Result<FormatArgs, ()> {
161161
let msg = "format argument must be a string literal";
162-
let fmt_span = efmt.span;
162+
let unexpanded_fmt_span = efmt.span;
163163
let (fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string(ecx, efmt, msg) {
164164
Ok(mut fmt) if append_newline => {
165165
fmt.0 = Symbol::intern(&format!("{}\n", fmt.0));
@@ -174,7 +174,7 @@ pub fn make_format_args(
174174
};
175175
if !suggested {
176176
err.span_suggestion(
177-
fmt_span.shrink_to_lo(),
177+
unexpanded_fmt_span.shrink_to_lo(),
178178
"you might be missing a string literal to format with",
179179
format!("\"{}\", ", sugg_fmt),
180180
Applicability::MaybeIncorrect,
@@ -192,7 +192,7 @@ pub fn make_format_args(
192192
};
193193

194194
let fmt_str = fmt_str.as_str(); // for the suggestions below
195-
let fmt_snippet = ecx.source_map().span_to_snippet(fmt_span).ok();
195+
let fmt_snippet = ecx.source_map().span_to_snippet(unexpanded_fmt_span).ok();
196196
let mut parser = parse::Parser::new(
197197
fmt_str,
198198
str_style,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// force-host
2+
// no-prefer-dynamic
3+
4+
#![crate_type = "proc-macro"]
5+
6+
extern crate proc_macro;
7+
8+
use proc_macro::{Literal, Span, TokenStream, TokenTree};
9+
10+
#[proc_macro]
11+
pub fn foo_with_input_span(input: TokenStream) -> TokenStream {
12+
let span = input.into_iter().next().unwrap().span();
13+
14+
let mut lit = Literal::string("{foo}");
15+
lit.set_span(span);
16+
17+
TokenStream::from(TokenTree::Literal(lit))
18+
}
19+
20+
#[proc_macro]
21+
pub fn err_with_input_span(input: TokenStream) -> TokenStream {
22+
let span = input.into_iter().next().unwrap().span();
23+
24+
let mut lit = Literal::string(" }");
25+
lit.set_span(span);
26+
27+
TokenStream::from(TokenTree::Literal(lit))
28+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
1+
// aux-build:format-string-proc-macro.rs
2+
3+
#[macro_use]
4+
extern crate format_string_proc_macro;
5+
6+
macro_rules! def_site {
7+
() => { "{foo}" } //~ ERROR: there is no argument named `foo`
8+
}
9+
10+
macro_rules! call_site {
11+
($fmt:literal) => { $fmt }
12+
}
13+
114
fn main() {
215
format!(concat!("{foo}")); //~ ERROR: there is no argument named `foo`
316
format!(concat!("{ba", "r} {}"), 1); //~ ERROR: there is no argument named `bar`
17+
18+
format!(def_site!());
19+
format!(call_site!("{foo}")); //~ ERROR: there is no argument named `foo`
20+
21+
format!(foo_with_input_span!("")); //~ ERROR: there is no argument named `foo`
422
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: there is no argument named `foo`
2-
--> $DIR/format-args-capture-macro-hygiene.rs:2:13
2+
--> $DIR/format-args-capture-macro-hygiene.rs:15:13
33
|
44
LL | format!(concat!("{foo}"));
55
| ^^^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | format!(concat!("{foo}"));
99
= note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info)
1010

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

21-
error: aborting due to 2 previous errors
21+
error: there is no argument named `foo`
22+
--> $DIR/format-args-capture-macro-hygiene.rs:7:13
23+
|
24+
LL | () => { "{foo}" }
25+
| ^^^^^^^
26+
...
27+
LL | format!(def_site!());
28+
| ----------- in this macro invocation
29+
|
30+
= note: did you intend to capture a variable `foo` from the surrounding scope?
31+
= note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro
32+
= note: this error originates in the macro `def_site` (in Nightly builds, run with -Z macro-backtrace for more info)
33+
34+
error: there is no argument named `foo`
35+
--> $DIR/format-args-capture-macro-hygiene.rs:19:24
36+
|
37+
LL | format!(call_site!("{foo}"));
38+
| ^^^^^^^
39+
|
40+
= note: did you intend to capture a variable `foo` from the surrounding scope?
41+
= note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro
42+
43+
error: there is no argument named `foo`
44+
--> $DIR/format-args-capture-macro-hygiene.rs:21:34
45+
|
46+
LL | format!(foo_with_input_span!(""));
47+
| ^^
48+
|
49+
= note: did you intend to capture a variable `foo` from the surrounding scope?
50+
= note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro
51+
52+
error: aborting due to 5 previous errors
2253

src/test/ui/fmt/format-concat-span.stderr

-11
This file was deleted.

src/test/ui/fmt/format-concat-span.rs src/test/ui/fmt/format-expanded-string.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
// aux-build:format-string-proc-macro.rs
2+
3+
#[macro_use]
4+
extern crate format_string_proc_macro;
5+
6+
17
// If the format string is another macro invocation, rustc would previously
28
// compute nonsensical spans, such as:
39
//
@@ -12,4 +18,7 @@
1218
fn main() {
1319
format!(concat!("abc}"));
1420
//~^ ERROR: invalid format string: unmatched `}` found
21+
22+
format!(err_with_input_span!(""));
23+
//~^ ERROR: invalid format string: unmatched `}` found
1524
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: invalid format string: unmatched `}` found
2+
--> $DIR/format-expanded-string.rs:19:13
3+
|
4+
LL | format!(concat!("abc}"));
5+
| ^^^^^^^^^^^^^^^ unmatched `}` in format string
6+
|
7+
= note: if you intended to print `}`, you can escape it using `}}`
8+
= note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info)
9+
10+
error: invalid format string: unmatched `}` found
11+
--> $DIR/format-expanded-string.rs:22:34
12+
|
13+
LL | format!(err_with_input_span!(""));
14+
| ^^ unmatched `}` in format string
15+
|
16+
= note: if you intended to print `}`, you can escape it using `}}`
17+
18+
error: aborting due to 2 previous errors
19+

0 commit comments

Comments
 (0)