diff options
| author | Manish Goregaokar <manishsmail@gmail.com> | 2020-07-17 14:09:06 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-07-17 14:09:06 -0700 |
| commit | 9c84c6b836bce0730cd1c34ef7e4832b755a713d (patch) | |
| tree | f6a1fb5c9b54aeec7369c86057b86083332bb82a /src/libcore | |
| parent | be3b972820c1e4793f246746d815eeea5e736dc9 (diff) | |
| parent | 9c3353b97ca9c7936a95dff4e26e181c895e6fc1 (diff) | |
Rollup merge of #74056 - fusion-engineering-forks:fmt-arguments-as-str, r=Amanieu
Add Arguments::as_str().
There exist quite a few macros in the Rust ecosystem which use `format_args!()` for formatting, but special case the one-argument case for optimization:
```rust
#[macro_export]
macro_rules! some_macro {
($s:expr) => { /* print &str directly, no formatting, no buffers */ };
($s:expr, $($tt:tt)*) => { /* use format_args to write to a buffer first */ }
}
```
E.g. [here](https://github.com/rust-embedded/cortex-m-semihosting/blob/7a961f0fbe6eb1b29a7ebde4bad4b9cf5f842b31/src/macros.rs#L48-L58), [here](https://github.com/rust-lang-nursery/failure/blob/20f9a9e223b7cd71aed541d050cc73a747fc00c4/src/macros.rs#L9-L17), and [here](https://github.com/fusion-engineering/px4-rust/blob/7b679cd6da9ffd95f36f6526d88345f8b36121da/px4/src/logging.rs#L45-L52).
The problem with these is that a forgotten argument such as in `some_macro!("{}")` will not be diagnosed, but just prints `"{}"`.
With this PR, it is possible to handle the no-arguments case separately *after* `format_args!()`, while simplifying the macro. Then these macros can give the proper error about a missing argument, just like `print!("{}")` does, while still using the same optimized implementation as before.
This is even more important with [RFC 2795](https://github.com/rust-lang/rfcs/pull/2795), to make sure `some_macro!("{some_variable}")` works as expected.
Diffstat (limited to 'src/libcore')
| -rw-r--r-- | src/libcore/fmt/mod.rs | 47 | ||||
| -rw-r--r-- | src/libcore/macros/mod.rs | 5 | ||||
| -rw-r--r-- | src/libcore/panicking.rs | 2 |
3 files changed, 49 insertions, 5 deletions
diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 9c5dbb5e6f3..638e83c3b93 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -324,7 +324,7 @@ impl<'a> Arguments<'a> { #[doc(hidden)] #[inline] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn new_v1(pieces: &'a [&'a str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> { + pub fn new_v1(pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> { Arguments { pieces, fmt: None, args } } @@ -338,7 +338,7 @@ impl<'a> Arguments<'a> { #[inline] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] pub fn new_v1_formatted( - pieces: &'a [&'a str], + pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>], fmt: &'a [rt::v1::Argument], ) -> Arguments<'a> { @@ -399,7 +399,7 @@ impl<'a> Arguments<'a> { #[derive(Copy, Clone)] pub struct Arguments<'a> { // Format string pieces to print. - pieces: &'a [&'a str], + pieces: &'a [&'static str], // Placeholder specs, or `None` if all specs are default (as in "{}{}"). fmt: Option<&'a [rt::v1::Argument]>, @@ -409,6 +409,47 @@ pub struct Arguments<'a> { args: &'a [ArgumentV1<'a>], } +impl<'a> Arguments<'a> { + /// Get the formatted string, if it has no arguments to be formatted. + /// + /// This can be used to avoid allocations in the most trivial case. + /// + /// # Examples + /// + /// ```rust + /// #![feature(fmt_as_str)] + /// + /// use core::fmt::Arguments; + /// + /// fn write_str(_: &str) { /* ... */ } + /// + /// fn write_fmt(args: &Arguments) { + /// if let Some(s) = args.as_str() { + /// write_str(s) + /// } else { + /// write_str(&args.to_string()); + /// } + /// } + /// ``` + /// + /// ```rust + /// #![feature(fmt_as_str)] + /// + /// assert_eq!(format_args!("hello").as_str(), Some("hello")); + /// assert_eq!(format_args!("").as_str(), Some("")); + /// assert_eq!(format_args!("{}", 1).as_str(), None); + /// ``` + #[unstable(feature = "fmt_as_str", issue = "74442")] + #[inline] + pub fn as_str(&self) -> Option<&'static str> { + match (self.pieces, self.args) { + ([], []) => Some(""), + ([s], []) => Some(s), + _ => None, + } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Debug for Arguments<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { diff --git a/src/libcore/macros/mod.rs b/src/libcore/macros/mod.rs index 17f7349bac2..4ac366ab164 100644 --- a/src/libcore/macros/mod.rs +++ b/src/libcore/macros/mod.rs @@ -6,9 +6,12 @@ macro_rules! panic { () => ( $crate::panic!("explicit panic") ); - ($msg:expr) => ( + ($msg:literal) => ( $crate::panicking::panic($msg) ); + ($msg:expr) => ( + $crate::panic!("{}", $crate::convert::identity::<&str>($msg)) + ); ($msg:expr,) => ( $crate::panic!($msg) ); diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 766c69a5f94..15fd638bef8 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -36,7 +36,7 @@ use crate::panic::{Location, PanicInfo}; #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[track_caller] #[lang = "panic"] // needed by codegen for panic on overflow and other `Assert` MIR terminators -pub fn panic(expr: &str) -> ! { +pub fn panic(expr: &'static str) -> ! { if cfg!(feature = "panic_immediate_abort") { super::intrinsics::abort() } |
