Skip to content

Commit c26746a

Browse files
committed
Auto merge of #90473 - joshtriplett:stabilize-format-args-capture, r=Mark-Simulacrum
stabilize format args capture Works as expected, and there are widespread reports of success with it, as well as interest in it. RFC: rust-lang/rfcs#2795 Tracking issue: #67984 Addressing items from the tracking issue: - We don't support capturing arguments from a non-literal format string like `format_args!(concat!(...))`. We could add that in a future enhancement, or we can decide that it isn't supported (as suggested in #67984 (comment) ). - I've updated the documentation. - `panic!` now supports capture as well. - There are potentially opportunities to further improve diagnostics for invalid usage, such as if it looks like the user tried to use an expression rather than a variable. However, such cases are all already caught and provide reasonable syntax errors now, and we can always provided even friendlier diagnostics in the future.
2 parents eab2d75 + afa719e commit c26746a

File tree

22 files changed

+83
-167
lines changed

22 files changed

+83
-167
lines changed

compiler/rustc_borrowck/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![feature(bool_to_option)]
44
#![feature(box_patterns)]
55
#![feature(crate_visibility_modifier)]
6-
#![feature(format_args_capture)]
6+
#![cfg_attr(bootstrap, feature(format_args_capture))]
77
#![feature(in_band_lifetimes)]
88
#![feature(iter_zip)]
99
#![feature(let_else)]

compiler/rustc_builtin_macros/src/format.rs

+10-26
Original file line numberDiff line numberDiff line change
@@ -527,17 +527,9 @@ impl<'a, 'b> Context<'a, 'b> {
527527
self.verify_arg_type(Exact(idx), ty)
528528
}
529529
None => {
530-
let capture_feature_enabled = self
531-
.ecx
532-
.ecfg
533-
.features
534-
.map_or(false, |features| features.format_args_capture);
535-
536530
// For the moment capturing variables from format strings expanded from macros is
537531
// disabled (see RFC #2795)
538-
let can_capture = capture_feature_enabled && self.is_literal;
539-
540-
if can_capture {
532+
if self.is_literal {
541533
// Treat this name as a variable to capture from the surrounding scope
542534
let idx = self.args.len();
543535
self.arg_types.push(Vec::new());
@@ -559,23 +551,15 @@ impl<'a, 'b> Context<'a, 'b> {
559551
};
560552
let mut err = self.ecx.struct_span_err(sp, &msg[..]);
561553

562-
if capture_feature_enabled && !self.is_literal {
563-
err.note(&format!(
564-
"did you intend to capture a variable `{}` from \
565-
the surrounding scope?",
566-
name
567-
));
568-
err.note(
569-
"to avoid ambiguity, `format_args!` cannot capture variables \
570-
when the format string is expanded from a macro",
571-
);
572-
} else if self.ecx.parse_sess().unstable_features.is_nightly_build() {
573-
err.help(&format!(
574-
"if you intended to capture `{}` from the surrounding scope, add \
575-
`#![feature(format_args_capture)]` to the crate attributes",
576-
name
577-
));
578-
}
554+
err.note(&format!(
555+
"did you intend to capture a variable `{}` from \
556+
the surrounding scope?",
557+
name
558+
));
559+
err.note(
560+
"to avoid ambiguity, `format_args!` cannot capture variables \
561+
when the format string is expanded from a macro",
562+
);
579563

580564
err.emit();
581565
}

compiler/rustc_errors/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#![feature(crate_visibility_modifier)]
77
#![feature(backtrace)]
88
#![feature(if_let_guard)]
9-
#![feature(format_args_capture)]
9+
#![cfg_attr(bootstrap, feature(format_args_capture))]
1010
#![feature(iter_zip)]
1111
#![feature(let_else)]
1212
#![feature(nll)]

compiler/rustc_expand/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![feature(crate_visibility_modifier)]
22
#![feature(decl_macro)]
33
#![feature(destructuring_assignment)]
4-
#![feature(format_args_capture)]
4+
#![cfg_attr(bootstrap, feature(format_args_capture))]
55
#![feature(if_let_guard)]
66
#![feature(iter_zip)]
77
#![feature(let_else)]

compiler/rustc_feature/src/accepted.rs

+2
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ declare_features! (
301301
(accepted, relaxed_struct_unsize, "1.58.0", Some(81793), None),
302302
/// Allows dereferencing raw pointers during const eval.
303303
(accepted, const_raw_ptr_deref, "1.58.0", Some(51911), None),
304+
/// Allows capturing variables in scope using format_args!
305+
(accepted, format_args_capture, "1.58.0", Some(67984), None),
304306

305307
// -------------------------------------------------------------------------
306308
// feature-group-end: accepted features

compiler/rustc_feature/src/active.rs

-3
Original file line numberDiff line numberDiff line change
@@ -539,9 +539,6 @@ declare_features! (
539539
/// Be more precise when looking for live drops in a const context.
540540
(active, const_precise_live_drops, "1.46.0", Some(73255), None),
541541

542-
/// Allows capturing variables in scope using format_args!
543-
(active, format_args_capture, "1.46.0", Some(67984), None),
544-
545542
/// Allows `if let` guard in match arms.
546543
(active, if_let_guard, "1.47.0", Some(51114), None),
547544

compiler/rustc_lint/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
#![feature(bool_to_option)]
3131
#![feature(box_patterns)]
3232
#![feature(crate_visibility_modifier)]
33-
#![feature(format_args_capture)]
33+
#![cfg_attr(bootstrap, feature(format_args_capture))]
3434
#![feature(iter_order_by)]
3535
#![feature(iter_zip)]
3636
#![feature(never_type)]

compiler/rustc_passes/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
88
#![feature(crate_visibility_modifier)]
99
#![feature(in_band_lifetimes)]
10-
#![feature(format_args_capture)]
10+
#![cfg_attr(bootstrap, feature(format_args_capture))]
1111
#![feature(iter_zip)]
1212
#![feature(map_try_insert)]
1313
#![feature(min_specialization)]

compiler/rustc_resolve/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#![feature(drain_filter)]
1414
#![feature(bool_to_option)]
1515
#![feature(crate_visibility_modifier)]
16-
#![feature(format_args_capture)]
16+
#![cfg_attr(bootstrap, feature(format_args_capture))]
1717
#![feature(iter_zip)]
1818
#![feature(let_else)]
1919
#![feature(never_type)]

compiler/rustc_typeck/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ This API is completely unstable and subject to change.
5858
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
5959
#![feature(bool_to_option)]
6060
#![feature(crate_visibility_modifier)]
61-
#![feature(format_args_capture)]
61+
#![cfg_attr(bootstrap, feature(format_args_capture))]
6262
#![feature(if_let_guard)]
6363
#![feature(in_band_lifetimes)]
6464
#![feature(is_sorted)]

library/alloc/src/fmt.rs

+17
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
//! format!("The number is {}", 1); // => "The number is 1"
1818
//! format!("{:?}", (3, 4)); // => "(3, 4)"
1919
//! format!("{value}", value=4); // => "4"
20+
//! let people = "Rustaceans";
21+
//! format!("Hello {people}!"); // => "Hello Rustaceans!"
2022
//! format!("{} {}", 1, 2); // => "1 2"
2123
//! format!("{:04}", 42); // => "0042" with leading zeros
2224
//! format!("{:#?}", (100, 200)); // => "(
@@ -80,6 +82,19 @@
8082
//! format!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b"
8183
//! ```
8284
//!
85+
//! If a named parameter does not appear in the argument list, `format!` will
86+
//! reference a variable with that name in the current scope.
87+
//!
88+
//! ```
89+
//! let argument = 2 + 2;
90+
//! format!("{argument}"); // => "4"
91+
//!
92+
//! fn make_string(a: u32, b: &str) -> String {
93+
//! format!("{b} {a}")
94+
//! }
95+
//! make_string(927, "label"); // => "label 927"
96+
//! ```
97+
//!
8398
//! It is not valid to put positional parameters (those without names) after
8499
//! arguments that have names. Like with positional parameters, it is not
85100
//! valid to provide named parameters that are unused by the format string.
@@ -98,6 +113,8 @@
98113
//! println!("Hello {:1$}!", "x", 5);
99114
//! println!("Hello {1:0$}!", 5, "x");
100115
//! println!("Hello {:width$}!", "x", width = 5);
116+
//! let width = 5;
117+
//! println!("Hello {:width$}!", "x");
101118
//! ```
102119
//!
103120
//! This is a parameter for the "minimum width" that the format should take up.

library/alloc/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
#![feature(fmt_internals)]
106106
#![feature(fn_traits)]
107107
#![feature(inherent_ascii_escape)]
108+
#![cfg_attr(bootstrap, feature(format_args_capture))]
108109
#![feature(inplace_iteration)]
109110
#![feature(iter_advance_by)]
110111
#![feature(iter_zip)]

src/doc/unstable-book/src/library-features/format-args-capture.md

-47
This file was deleted.

src/test/ui/fmt/feature-gate-format-args-capture.rs

-6
This file was deleted.

src/test/ui/fmt/feature-gate-format-args-capture.stderr

-18
This file was deleted.

src/test/ui/fmt/format-args-capture-macro-hygiene.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#![feature(format_args_capture)]
2-
31
fn main() {
42
format!(concat!("{foo}")); //~ ERROR: there is no argument named `foo`
53
format!(concat!("{ba", "r} {}"), 1); //~ ERROR: there is no argument named `bar`

src/test/ui/fmt/format-args-capture-macro-hygiene.stderr

+2-2
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:4:13
2+
--> $DIR/format-args-capture-macro-hygiene.rs:2: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:5:13
12+
--> $DIR/format-args-capture-macro-hygiene.rs:3:13
1313
|
1414
LL | format!(concat!("{ba", "r} {}"), 1);
1515
| ^^^^^^^^^^^^^^^^^^^^^^^

src/test/ui/fmt/format-args-capture-missing-variables.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#![feature(format_args_capture)]
2-
31
fn main() {
42
format!("{} {foo} {} {bar} {}", 1, 2, 3);
53
//~^ ERROR: cannot find value `foo` in this scope

src/test/ui/fmt/format-args-capture-missing-variables.stderr

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,43 @@
11
error: named argument never used
2-
--> $DIR/format-args-capture-missing-variables.rs:10:51
2+
--> $DIR/format-args-capture-missing-variables.rs:8:51
33
|
44
LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
55
| ------------------- ^ named argument never used
66
| |
77
| formatting specifier missing
88

99
error[E0425]: cannot find value `foo` in this scope
10-
--> $DIR/format-args-capture-missing-variables.rs:4:17
10+
--> $DIR/format-args-capture-missing-variables.rs:2:17
1111
|
1212
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
1313
| ^^^^^ not found in this scope
1414

1515
error[E0425]: cannot find value `bar` in this scope
16-
--> $DIR/format-args-capture-missing-variables.rs:4:26
16+
--> $DIR/format-args-capture-missing-variables.rs:2:26
1717
|
1818
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
1919
| ^^^^^ not found in this scope
2020

2121
error[E0425]: cannot find value `foo` in this scope
22-
--> $DIR/format-args-capture-missing-variables.rs:8:14
22+
--> $DIR/format-args-capture-missing-variables.rs:6:14
2323
|
2424
LL | format!("{foo}");
2525
| ^^^^^ not found in this scope
2626

2727
error[E0425]: cannot find value `valueb` in this scope
28-
--> $DIR/format-args-capture-missing-variables.rs:10:23
28+
--> $DIR/format-args-capture-missing-variables.rs:8:23
2929
|
3030
LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
3131
| ^^^^^^^^ not found in this scope
3232

3333
error[E0425]: cannot find value `foo` in this scope
34-
--> $DIR/format-args-capture-missing-variables.rs:16:9
34+
--> $DIR/format-args-capture-missing-variables.rs:14:9
3535
|
3636
LL | {foo}
3737
| ^^^^^ not found in this scope
3838

3939
error[E0425]: cannot find value `foo` in this scope
40-
--> $DIR/format-args-capture-missing-variables.rs:21:13
40+
--> $DIR/format-args-capture-missing-variables.rs:19:13
4141
|
4242
LL | panic!("{foo} {bar}", bar=1);
4343
| ^^^^^ not found in this scope

src/test/ui/fmt/format-args-capture.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// run-pass
2-
#![feature(format_args_capture)]
32
#![feature(cfg_panic)]
43

54
fn main() {

src/test/ui/fmt/ifmt-bad-arg.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ fn main() {
2525
//~^ ERROR: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments)
2626

2727
format!("{} {foo} {} {bar} {}", 1, 2, 3);
28-
//~^ ERROR: there is no argument named `foo`
29-
//~^^ ERROR: there is no argument named `bar`
28+
//~^ ERROR: cannot find value `foo` in this scope
29+
//~^^ ERROR: cannot find value `bar` in this scope
3030

31-
format!("{foo}"); //~ ERROR: no argument named `foo`
31+
format!("{foo}"); //~ ERROR: cannot find value `foo` in this scope
3232
format!("", 1, 2); //~ ERROR: multiple unused formatting arguments
3333
format!("{}", 1, 2); //~ ERROR: argument never used
3434
format!("{1}", 1, 2); //~ ERROR: argument never used
@@ -43,7 +43,7 @@ fn main() {
4343
// bad named arguments, #35082
4444

4545
format!("{valuea} {valueb}", valuea=5, valuec=7);
46-
//~^ ERROR there is no argument named `valueb`
46+
//~^ ERROR cannot find value `valueb` in this scope
4747
//~^^ ERROR named argument never used
4848

4949
// bad syntax of the format string
@@ -60,7 +60,7 @@ fn main() {
6060
{foo}
6161
6262
"##);
63-
//~^^^ ERROR: there is no argument named `foo`
63+
//~^^^ ERROR: cannot find value `foo` in this scope
6464

6565
// bad syntax in format string with multiple newlines, #53836
6666
format!("first number: {}

0 commit comments

Comments
 (0)