-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Properly allow macro expanded
format_args
invocations to uses captures
Originally, this was kinda half-allowed. There were some primitive checks in place that looked at the span to see whether the input was likely a literal. These "source literal" checks are needed because the spans created during `format_args` parsing only make sense when it is indeed a literal that was written in the source code directly. This is orthogonal to the restriction that the first argument must be a "direct literal", not being exanpanded from macros. This restriction was imposed by [RFC 2795] on the basis of being too confusing. But this was only concerned with the argument of the invocation being a literal, not whether it was a source literal (maybe in spirit it meant it being a source literal, this is not clear to me). Since the original check only really cared about source literals (which is good enough to deny the `format_args!(concat!())` example), macros expanding to `format_args` invocations were able to use implicit captures if they spanned the string in a way that lead back to a source string. The "source literal" checks were not strict enough and caused ICEs in certain cases (see # 106191 (the space is intended to avoid spammy backreferences)). So I tightened it up in # 106195 to really only work if it's a direct source literal. This caused the `indoc` crate to break. `indoc` transformed the source literal by removing whitespace, which made it not a "source literal" anymore (which is required to fix the ICE). But since `indoc` spanned the literal in ways that made the old check think that it's a literal, it was able to use implicit captures (which is useful and nice for the users of `indoc`). This commit properly seperates the previously introduced concepts of "source literal" and "direct literal" and therefore allows `indoc` invocations, which don't create "source literals" to use implicit captures again. [RFC 2795]: https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html#macro-hygiene
- Loading branch information
Showing
11 changed files
with
198 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
tests/ui/fmt/format-args-capture-first-literal-is-macro.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// aux-build:format-string-proc-macro.rs | ||
|
||
#[macro_use] | ||
extern crate format_string_proc_macro; | ||
|
||
macro_rules! identity_mbe { | ||
($tt:tt) => { | ||
$tt | ||
//~^ ERROR there is no argument named `a` | ||
}; | ||
} | ||
|
||
fn main() { | ||
let a = 0; | ||
|
||
format!(identity_pm!("{a}")); | ||
//~^ ERROR there is no argument named `a` | ||
format!(identity_mbe!("{a}")); | ||
format!(concat!("{a}")); | ||
//~^ ERROR there is no argument named `a` | ||
} |
30 changes: 30 additions & 0 deletions
30
tests/ui/fmt/format-args-capture-first-literal-is-macro.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
error: there is no argument named `a` | ||
--> $DIR/format-args-capture-first-literal-is-macro.rs:16:26 | ||
| | ||
LL | format!(identity_pm!("{a}")); | ||
| ^^^^^ | ||
| | ||
= note: did you intend to capture a variable `a` 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 `a` | ||
--> $DIR/format-args-capture-first-literal-is-macro.rs:8:9 | ||
| | ||
LL | $tt | ||
| ^^^ | ||
| | ||
= note: did you intend to capture a variable `a` 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 `a` | ||
--> $DIR/format-args-capture-first-literal-is-macro.rs:19:13 | ||
| | ||
LL | format!(concat!("{a}")); | ||
| ^^^^^^^^^^^^^^ | ||
| | ||
= note: did you intend to capture a variable `a` 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 `concat` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
||
error: aborting due to 3 previous errors | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// aux-build:format-string-proc-macro.rs | ||
|
||
extern crate format_string_proc_macro; | ||
|
||
fn main() { | ||
format_string_proc_macro::bad_format_args_captures!(); | ||
//~^ ERROR there is no argument named `x` | ||
} |
12 changes: 12 additions & 0 deletions
12
tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
error: there is no argument named `x` | ||
--> $DIR/format-args-capture-from-pm-first-arg-macro.rs:6:5 | ||
| | ||
LL | format_string_proc_macro::bad_format_args_captures!(); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= note: did you intend to capture a variable `x` 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 `concat` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
||
error: aborting due to previous error | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// check-pass | ||
// aux-build:format-string-proc-macro.rs | ||
|
||
extern crate format_string_proc_macro; | ||
|
||
fn main() { | ||
// While literal macros like `format_args!(concat!())` are not supposed to work with implicit | ||
// captures, it should work if the whole invocation comes from a macro expansion (#106408). | ||
format_string_proc_macro::format_args_captures!(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// run-pass | ||
|
||
macro_rules! format_mbe { | ||
($tt:tt) => { | ||
{ | ||
#[allow(unused_variables)] | ||
let a = 123; | ||
format!($tt) | ||
} | ||
}; | ||
} | ||
|
||
fn main() { | ||
let a = 0; | ||
assert_eq!(format_mbe!("{a}"), "0"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,10 @@ | ||
// aux-build:format-string-proc-macro.rs | ||
// check-fail | ||
// known-bug: #106191 | ||
// unset-rustc-env:RUST_BACKTRACE | ||
// had to be reverted | ||
// error-pattern:unexpectedly panicked | ||
// failure-status:101 | ||
// dont-check-compiler-stderr | ||
|
||
extern crate format_string_proc_macro; | ||
|
||
fn main() { | ||
format_string_proc_macro::respan_to_invalid_format_literal!("¡"); | ||
//~^ ERROR invalid format string: expected `'}'` but string was terminated | ||
format_args!(r#concat!("¡ {")); | ||
//~^ ERROR invalid format string: expected `'}'` but string was terminated | ||
} |
Oops, something went wrong.