about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2020-07-17 14:09:06 -0700
committerGitHub <noreply@github.com>2020-07-17 14:09:06 -0700
commit9c84c6b836bce0730cd1c34ef7e4832b755a713d (patch)
treef6a1fb5c9b54aeec7369c86057b86083332bb82a /src/libcore
parentbe3b972820c1e4793f246746d815eeea5e736dc9 (diff)
parent9c3353b97ca9c7936a95dff4e26e181c895e6fc1 (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.rs47
-rw-r--r--src/libcore/macros/mod.rs5
-rw-r--r--src/libcore/panicking.rs2
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()
     }