Skip to content

Commit 4ff55ec

Browse files
authored
Rollup merge of #86104 - FabianWolff:issue-86085, r=davidtwco
Fix span calculation in format strings This pull request fixes #86085. The ICE described there is due to an error in the span calculation inside format strings, if the format string is the result of a macro invocation: ```rust fn main() { format!(concat!("abc}")); } ``` currently produces: ``` error: invalid format string: unmatched `}` found --> test.rs:2:17 | 2 | format!(concat!("abc}")); | ^ unmatched `}` in format string ``` which is obviously incorrect. This happens because the span of the entire `concat!()` is combined with the _relative_ location of the unmatched `` `}` `` in the _result_ of the macro invocation (i.e. 4). In #86085, this has led to a span that starts or ends in the middle of a multibyte character, but the root cause was the same. This pull request fixes the problem.
2 parents 7030efb + 14f3ec2 commit 4ff55ec

File tree

5 files changed

+57
-1
lines changed

5 files changed

+57
-1
lines changed

compiler/rustc_builtin_macros/src/format.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,7 @@ pub fn expand_preparsed_format_args(
939939

940940
let msg = "format argument must be a string literal";
941941
let fmt_sp = efmt.span;
942+
let efmt_kind_is_lit: bool = matches!(efmt.kind, ast::ExprKind::Lit(_));
942943
let (fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string(ecx, efmt, msg) {
943944
Ok(mut fmt) if append_newline => {
944945
fmt.0 = Symbol::intern(&format!("{}\n", fmt.0));
@@ -989,7 +990,19 @@ pub fn expand_preparsed_format_args(
989990

990991
if !parser.errors.is_empty() {
991992
let err = parser.errors.remove(0);
992-
let sp = fmt_span.from_inner(err.span);
993+
let sp = if efmt_kind_is_lit {
994+
fmt_span.from_inner(err.span)
995+
} else {
996+
// The format string could be another macro invocation, e.g.:
997+
// format!(concat!("abc", "{}"), 4);
998+
// However, `err.span` is an inner span relative to the *result* of
999+
// the macro invocation, which is why we would get a nonsensical
1000+
// result calling `fmt_span.from_inner(err.span)` as above, and
1001+
// might even end up inside a multibyte character (issue #86085).
1002+
// Therefore, we conservatively report the error for the entire
1003+
// argument span here.
1004+
fmt_span
1005+
};
9931006
let mut e = ecx.struct_span_err(sp, &format!("invalid format string: {}", err.description));
9941007
e.span_label(sp, err.label + " in format string");
9951008
if let Some(note) = err.note {

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

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// If the format string is another macro invocation, rustc would previously
2+
// compute nonsensical spans, such as:
3+
//
4+
// error: invalid format string: unmatched `}` found
5+
// --> test.rs:2:17
6+
// |
7+
// 2 | format!(concat!("abc}"));
8+
// | ^ unmatched `}` in format string
9+
//
10+
// This test checks that this behavior has been fixed.
11+
12+
fn main() {
13+
format!(concat!("abc}"));
14+
//~^ ERROR: invalid format string: unmatched `}` found
15+
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: invalid format string: unmatched `}` found
2+
--> $DIR/format-concat-span.rs:13: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: aborting due to previous error
11+

src/test/ui/fmt/issue-86085.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Tests for an ICE with the fuzzed input below.
2+
3+
fn main ( ) {
4+
format ! ( concat ! ( r#"lJ𐏿Æ�.𐏿�"# , "r} {}" ) ) ;
5+
//~^ ERROR: invalid format string: unmatched `}` found
6+
}

src/test/ui/fmt/issue-86085.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: invalid format string: unmatched `}` found
2+
--> $DIR/issue-86085.rs:4:12
3+
|
4+
LL | format ! ( concat ! ( r#"lJ𐏿Æ�.𐏿�"# , "r} {}" ) ) ;
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: aborting due to previous error
11+

0 commit comments

Comments
 (0)