diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index aca7d3174f6cd..76d3a83b48daa 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -3,7 +3,7 @@ #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(crate_visibility_modifier)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(in_band_lifetimes)] #![feature(iter_zip)] #![feature(let_else)] diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 52b00a2bc7474..097eaddb87408 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -527,17 +527,9 @@ impl<'a, 'b> Context<'a, 'b> { self.verify_arg_type(Exact(idx), ty) } None => { - let capture_feature_enabled = self - .ecx - .ecfg - .features - .map_or(false, |features| features.format_args_capture); - // For the moment capturing variables from format strings expanded from macros is // disabled (see RFC #2795) - let can_capture = capture_feature_enabled && self.is_literal; - - if can_capture { + if self.is_literal { // Treat this name as a variable to capture from the surrounding scope let idx = self.args.len(); self.arg_types.push(Vec::new()); @@ -559,23 +551,15 @@ impl<'a, 'b> Context<'a, 'b> { }; let mut err = self.ecx.struct_span_err(sp, &msg[..]); - if capture_feature_enabled && !self.is_literal { - err.note(&format!( - "did you intend to capture a variable `{}` from \ - the surrounding scope?", - name - )); - err.note( - "to avoid ambiguity, `format_args!` cannot capture variables \ - when the format string is expanded from a macro", - ); - } else if self.ecx.parse_sess().unstable_features.is_nightly_build() { - err.help(&format!( - "if you intended to capture `{}` from the surrounding scope, add \ - `#![feature(format_args_capture)]` to the crate attributes", - name - )); - } + err.note(&format!( + "did you intend to capture a variable `{}` from \ + the surrounding scope?", + name + )); + err.note( + "to avoid ambiguity, `format_args!` cannot capture variables \ + when the format string is expanded from a macro", + ); err.emit(); } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 21a2eb771c8e2..bb3d3a415e7d5 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -6,7 +6,7 @@ #![feature(crate_visibility_modifier)] #![feature(backtrace)] #![feature(if_let_guard)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(iter_zip)] #![feature(let_else)] #![feature(nll)] diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 521ca2135c6f2..4e84a9df6c978 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,7 +1,7 @@ #![feature(crate_visibility_modifier)] #![feature(decl_macro)] #![feature(destructuring_assignment)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(if_let_guard)] #![feature(iter_zip)] #![feature(let_else)] diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 3bd1272c7cb47..3cb543fe3ab98 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -301,6 +301,8 @@ declare_features! ( (accepted, relaxed_struct_unsize, "1.58.0", Some(81793), None), /// Allows dereferencing raw pointers during const eval. (accepted, const_raw_ptr_deref, "1.58.0", Some(51911), None), + /// Allows capturing variables in scope using format_args! + (accepted, format_args_capture, "1.58.0", Some(67984), None), // ------------------------------------------------------------------------- // feature-group-end: accepted features diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index d8b4539d831bb..61dd505e1e912 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -539,9 +539,6 @@ declare_features! ( /// Be more precise when looking for live drops in a const context. (active, const_precise_live_drops, "1.46.0", Some(73255), None), - /// Allows capturing variables in scope using format_args! - (active, format_args_capture, "1.46.0", Some(67984), None), - /// Allows `if let` guard in match arms. (active, if_let_guard, "1.47.0", Some(51114), None), diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index f6514ddca9f57..507b4421fa160 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -30,7 +30,7 @@ #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(crate_visibility_modifier)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(iter_order_by)] #![feature(iter_zip)] #![feature(never_type)] diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 4adec3c4f608d..af1c724410037 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -7,7 +7,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(iter_zip)] #![feature(map_try_insert)] #![feature(min_specialization)] diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index f5bea83bdcf65..d17e8875a1ec0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -13,7 +13,7 @@ #![feature(drain_filter)] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(iter_zip)] #![feature(let_else)] #![feature(never_type)] diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index ba0fd12a2755f..0881cf07586b3 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -58,7 +58,7 @@ This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(if_let_guard)] #![feature(in_band_lifetimes)] #![feature(is_sorted)] diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index 50e789d76b7f3..b4d16d74db488 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -17,6 +17,8 @@ //! format!("The number is {}", 1); // => "The number is 1" //! format!("{:?}", (3, 4)); // => "(3, 4)" //! format!("{value}", value=4); // => "4" +//! let people = "Rustaceans"; +//! format!("Hello {people}!"); // => "Hello Rustaceans!" //! format!("{} {}", 1, 2); // => "1 2" //! format!("{:04}", 42); // => "0042" with leading zeros //! format!("{:#?}", (100, 200)); // => "( @@ -80,6 +82,19 @@ //! format!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" //! ``` //! +//! If a named parameter does not appear in the argument list, `format!` will +//! reference a variable with that name in the current scope. +//! +//! ``` +//! let argument = 2 + 2; +//! format!("{argument}"); // => "4" +//! +//! fn make_string(a: u32, b: &str) -> String { +//! format!("{b} {a}") +//! } +//! make_string(927, "label"); // => "label 927" +//! ``` +//! //! It is not valid to put positional parameters (those without names) after //! arguments that have names. Like with positional parameters, it is not //! valid to provide named parameters that are unused by the format string. @@ -98,6 +113,8 @@ //! println!("Hello {:1$}!", "x", 5); //! println!("Hello {1:0$}!", 5, "x"); //! println!("Hello {:width$}!", "x", width = 5); +//! let width = 5; +//! println!("Hello {:width$}!", "x"); //! ``` //! //! This is a parameter for the "minimum width" that the format should take up. diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 1bb257acff76a..4a66c3f6b2e26 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -105,6 +105,7 @@ #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(inherent_ascii_escape)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_zip)] diff --git a/src/doc/unstable-book/src/library-features/format-args-capture.md b/src/doc/unstable-book/src/library-features/format-args-capture.md deleted file mode 100644 index 64b1b3d81bd83..0000000000000 --- a/src/doc/unstable-book/src/library-features/format-args-capture.md +++ /dev/null @@ -1,47 +0,0 @@ -# `format_args_capture` - -The tracking issue for this feature is: [#67984] - -[#67984]: https://github.com/rust-lang/rust/issues/67984 - ------------------------- - -Enables `format_args!` (and macros which use `format_args!` in their implementation, such -as `format!`, `print!` and `panic!`) to capture variables from the surrounding scope. -This avoids the need to pass named parameters when the binding in question -already exists in scope. - -```rust -#![feature(format_args_capture)] - -let (person, species, name) = ("Charlie Brown", "dog", "Snoopy"); - -// captures named argument `person` -print!("Hello {person}"); - -// captures named arguments `species` and `name` -format!("The {species}'s name is {name}."); -``` - -This also works for formatting parameters such as width and precision: - -```rust -#![feature(format_args_capture)] - -let precision = 2; -let s = format!("{:.precision$}", 1.324223); - -assert_eq!(&s, "1.32"); -``` - -A non-exhaustive list of macros which benefit from this functionality include: -- `format!` -- `print!` and `println!` -- `eprint!` and `eprintln!` -- `write!` and `writeln!` -- `panic!` -- `unreachable!` -- `unimplemented!` -- `todo!` -- `assert!` and similar -- macros in many thirdparty crates, such as `log` diff --git a/src/test/ui/fmt/feature-gate-format-args-capture.rs b/src/test/ui/fmt/feature-gate-format-args-capture.rs deleted file mode 100644 index 21af91610915f..0000000000000 --- a/src/test/ui/fmt/feature-gate-format-args-capture.rs +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - format!("{foo}"); //~ ERROR: there is no argument named `foo` - - // panic! doesn't hit format_args! unless there are two or more arguments. - panic!("{foo} {bar}", bar=1); //~ ERROR: there is no argument named `foo` -} diff --git a/src/test/ui/fmt/feature-gate-format-args-capture.stderr b/src/test/ui/fmt/feature-gate-format-args-capture.stderr deleted file mode 100644 index f08f1651cb622..0000000000000 --- a/src/test/ui/fmt/feature-gate-format-args-capture.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error: there is no argument named `foo` - --> $DIR/feature-gate-format-args-capture.rs:2:14 - | -LL | format!("{foo}"); - | ^^^^^ - | - = help: if you intended to capture `foo` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes - -error: there is no argument named `foo` - --> $DIR/feature-gate-format-args-capture.rs:5:13 - | -LL | panic!("{foo} {bar}", bar=1); - | ^^^^^ - | - = help: if you intended to capture `foo` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes - -error: aborting due to 2 previous errors - 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 6ca7dcc216f3a..fdbd93836ef9f 100644 --- a/src/test/ui/fmt/format-args-capture-macro-hygiene.rs +++ b/src/test/ui/fmt/format-args-capture-macro-hygiene.rs @@ -1,5 +1,3 @@ -#![feature(format_args_capture)] - fn main() { format!(concat!("{foo}")); //~ ERROR: there is no argument named `foo` format!(concat!("{ba", "r} {}"), 1); //~ ERROR: there is no argument named `bar` 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 33cd89ad5a7aa..9423e8c819d7a 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:4:13 + --> $DIR/format-args-capture-macro-hygiene.rs:2: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:5:13 + --> $DIR/format-args-capture-macro-hygiene.rs:3:13 | LL | format!(concat!("{ba", "r} {}"), 1); | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/fmt/format-args-capture-missing-variables.rs b/src/test/ui/fmt/format-args-capture-missing-variables.rs index 3a4b6144b04db..46fc083cb7301 100644 --- a/src/test/ui/fmt/format-args-capture-missing-variables.rs +++ b/src/test/ui/fmt/format-args-capture-missing-variables.rs @@ -1,5 +1,3 @@ -#![feature(format_args_capture)] - fn main() { format!("{} {foo} {} {bar} {}", 1, 2, 3); //~^ ERROR: cannot find value `foo` in this scope diff --git a/src/test/ui/fmt/format-args-capture-missing-variables.stderr b/src/test/ui/fmt/format-args-capture-missing-variables.stderr index ec2faa4185b3e..d53c206003f94 100644 --- a/src/test/ui/fmt/format-args-capture-missing-variables.stderr +++ b/src/test/ui/fmt/format-args-capture-missing-variables.stderr @@ -1,5 +1,5 @@ error: named argument never used - --> $DIR/format-args-capture-missing-variables.rs:10:51 + --> $DIR/format-args-capture-missing-variables.rs:8:51 | LL | format!("{valuea} {valueb}", valuea=5, valuec=7); | ------------------- ^ named argument never used @@ -7,37 +7,37 @@ LL | format!("{valuea} {valueb}", valuea=5, valuec=7); | formatting specifier missing error[E0425]: cannot find value `foo` in this scope - --> $DIR/format-args-capture-missing-variables.rs:4:17 + --> $DIR/format-args-capture-missing-variables.rs:2:17 | LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); | ^^^^^ not found in this scope error[E0425]: cannot find value `bar` in this scope - --> $DIR/format-args-capture-missing-variables.rs:4:26 + --> $DIR/format-args-capture-missing-variables.rs:2:26 | LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); | ^^^^^ not found in this scope error[E0425]: cannot find value `foo` in this scope - --> $DIR/format-args-capture-missing-variables.rs:8:14 + --> $DIR/format-args-capture-missing-variables.rs:6:14 | LL | format!("{foo}"); | ^^^^^ not found in this scope error[E0425]: cannot find value `valueb` in this scope - --> $DIR/format-args-capture-missing-variables.rs:10:23 + --> $DIR/format-args-capture-missing-variables.rs:8:23 | LL | format!("{valuea} {valueb}", valuea=5, valuec=7); | ^^^^^^^^ not found in this scope error[E0425]: cannot find value `foo` in this scope - --> $DIR/format-args-capture-missing-variables.rs:16:9 + --> $DIR/format-args-capture-missing-variables.rs:14:9 | LL | {foo} | ^^^^^ not found in this scope error[E0425]: cannot find value `foo` in this scope - --> $DIR/format-args-capture-missing-variables.rs:21:13 + --> $DIR/format-args-capture-missing-variables.rs:19:13 | LL | panic!("{foo} {bar}", bar=1); | ^^^^^ not found in this scope diff --git a/src/test/ui/fmt/format-args-capture.rs b/src/test/ui/fmt/format-args-capture.rs index b30e9a47a13e8..e830a5bc9c5c8 100644 --- a/src/test/ui/fmt/format-args-capture.rs +++ b/src/test/ui/fmt/format-args-capture.rs @@ -1,5 +1,4 @@ // run-pass -#![feature(format_args_capture)] #![feature(cfg_panic)] fn main() { diff --git a/src/test/ui/fmt/ifmt-bad-arg.rs b/src/test/ui/fmt/ifmt-bad-arg.rs index a0b0a8fb98594..b3e54ed32aac7 100644 --- a/src/test/ui/fmt/ifmt-bad-arg.rs +++ b/src/test/ui/fmt/ifmt-bad-arg.rs @@ -25,10 +25,10 @@ fn main() { //~^ ERROR: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments) format!("{} {foo} {} {bar} {}", 1, 2, 3); - //~^ ERROR: there is no argument named `foo` - //~^^ ERROR: there is no argument named `bar` + //~^ ERROR: cannot find value `foo` in this scope + //~^^ ERROR: cannot find value `bar` in this scope - format!("{foo}"); //~ ERROR: no argument named `foo` + format!("{foo}"); //~ ERROR: cannot find value `foo` in this scope format!("", 1, 2); //~ ERROR: multiple unused formatting arguments format!("{}", 1, 2); //~ ERROR: argument never used format!("{1}", 1, 2); //~ ERROR: argument never used @@ -43,7 +43,7 @@ fn main() { // bad named arguments, #35082 format!("{valuea} {valueb}", valuea=5, valuec=7); - //~^ ERROR there is no argument named `valueb` + //~^ ERROR cannot find value `valueb` in this scope //~^^ ERROR named argument never used // bad syntax of the format string @@ -60,7 +60,7 @@ fn main() { {foo} "##); - //~^^^ ERROR: there is no argument named `foo` + //~^^^ ERROR: cannot find value `foo` in this scope // bad syntax in format string with multiple newlines, #53836 format!("first number: {} diff --git a/src/test/ui/fmt/ifmt-bad-arg.stderr b/src/test/ui/fmt/ifmt-bad-arg.stderr index f4c84e22faaa3..acc4e95f5bb7e 100644 --- a/src/test/ui/fmt/ifmt-bad-arg.stderr +++ b/src/test/ui/fmt/ifmt-bad-arg.stderr @@ -58,30 +58,6 @@ LL | format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2); | = note: positional arguments are zero-based -error: there is no argument named `foo` - --> $DIR/ifmt-bad-arg.rs:27:17 - | -LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); - | ^^^^^ - | - = help: if you intended to capture `foo` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes - -error: there is no argument named `bar` - --> $DIR/ifmt-bad-arg.rs:27:26 - | -LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); - | ^^^^^ - | - = help: if you intended to capture `bar` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes - -error: there is no argument named `foo` - --> $DIR/ifmt-bad-arg.rs:31:14 - | -LL | format!("{foo}"); - | ^^^^^ - | - = help: if you intended to capture `foo` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes - error: multiple unused formatting arguments --> $DIR/ifmt-bad-arg.rs:32:17 | @@ -156,14 +132,6 @@ LL | format!("{foo} {} {}", foo=1, 2); | | | named argument -error: there is no argument named `valueb` - --> $DIR/ifmt-bad-arg.rs:45:23 - | -LL | format!("{valuea} {valueb}", valuea=5, valuec=7); - | ^^^^^^^^ - | - = help: if you intended to capture `valueb` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes - error: named argument never used --> $DIR/ifmt-bad-arg.rs:45:51 | @@ -208,14 +176,6 @@ LL | format!("foo %s baz", "bar"); | = note: printf formatting not supported; see the documentation for `std::fmt` -error: there is no argument named `foo` - --> $DIR/ifmt-bad-arg.rs:60:9 - | -LL | {foo} - | ^^^^^ - | - = help: if you intended to capture `foo` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes - error: invalid format string: expected `'}'`, found `'t'` --> $DIR/ifmt-bad-arg.rs:75:1 | @@ -302,6 +262,36 @@ LL | println!("{:.*}"); = note: positional arguments are zero-based = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html +error[E0425]: cannot find value `foo` in this scope + --> $DIR/ifmt-bad-arg.rs:27:17 + | +LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); + | ^^^^^ not found in this scope + +error[E0425]: cannot find value `bar` in this scope + --> $DIR/ifmt-bad-arg.rs:27:26 + | +LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); + | ^^^^^ not found in this scope + +error[E0425]: cannot find value `foo` in this scope + --> $DIR/ifmt-bad-arg.rs:31:14 + | +LL | format!("{foo}"); + | ^^^^^ not found in this scope + +error[E0425]: cannot find value `valueb` in this scope + --> $DIR/ifmt-bad-arg.rs:45:23 + | +LL | format!("{valuea} {valueb}", valuea=5, valuec=7); + | ^^^^^^^^ not found in this scope + +error[E0425]: cannot find value `foo` in this scope + --> $DIR/ifmt-bad-arg.rs:60:9 + | +LL | {foo} + | ^^^^^ not found in this scope + error[E0308]: mismatched types --> $DIR/ifmt-bad-arg.rs:78:32 | @@ -324,4 +314,5 @@ LL | println!("{} {:07$.*} {}", 1, 3.2, 4); error: aborting due to 36 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0308, E0425. +For more information about an error, try `rustc --explain E0308`.