auto merge of #20136 : eddyb/rust/format-args, r=alexcrichton

We have the technology: no longer do you need to write closures to use `format_args!`.
This is a `[breaking-change]`, as it forces you to clean up old hacks - if you had code like this:
```rust
format_args!(fmt::format, "{} {} {}", a, b, c)
format_args!(|args| { w.write_fmt(args) }, "{} {} {}", x, y, z)
```
change it to this: 
```rust
fmt::format(format_args!("{} {} {}", a, b, c))
w.write_fmt(format_args!("{} {} {}", x, y, z))
```
To allow them to be called with `format_args!(...)` directly, several functions were modified to
take `fmt::Arguments` by value instead of by reference. Also, `fmt::Arguments` derives `Copy`
now in order to preserve all usecases that were previously possible.
This commit is contained in:
bors 2014-12-28 03:11:48 +00:00
commit 3e6b29f8ad
22 changed files with 725 additions and 237 deletions

View File

@ -945,6 +945,15 @@ pub trait ToString {
}
impl<T: fmt::Show> ToString for T {
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
fn to_string(&self) -> String {
let mut buf = Vec::<u8>::new();
let _ = fmt::write(&mut buf, format_args!("{}", *self));
String::from_utf8(buf).unwrap()
}
// NOTE(stage0): Remove method after a snapshot
#[cfg(stage0)]
fn to_string(&self) -> String {
let mut buf = Vec::<u8>::new();
let _ = format_args!(|args| fmt::write(&mut buf, args), "{}", self);

View File

@ -325,6 +325,13 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
let mut filler = Filler { buf: &mut buf, end: &mut end };
match sign {
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
SignNeg => {
let _ = fmt::write(&mut filler, format_args!("{:-}", exp));
}
// NOTE(stage0): Remove match arm after a snapshot
#[cfg(stage0)]
SignNeg => {
let _ = format_args!(|args| {
fmt::write(&mut filler, args)

View File

@ -70,6 +70,16 @@ pub trait FormatWriter {
/// This function will return an instance of `FormatError` on error.
fn write(&mut self, bytes: &[u8]) -> Result;
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// Glue for usage of the `write!` macro with implementers of this trait.
///
/// This method should generally not be invoked manually, but rather through
/// the `write!` macro itself.
fn write_fmt(&mut self, args: Arguments) -> Result { write(self, args) }
// NOTE(stage0): Remove method after a snapshot
#[cfg(stage0)]
/// Glue for usage of the `write!` macro with implementers of this trait.
///
/// This method should generally not be invoked manually, but rather through
@ -180,6 +190,7 @@ impl<'a> Arguments<'a> {
/// macro validates the format string at compile-time so usage of the `write`
/// and `format` functions can be safely performed.
#[stable]
#[deriving(Copy)]
pub struct Arguments<'a> {
// Format string pieces to print.
pieces: &'a [&'a str],
@ -193,6 +204,14 @@ pub struct Arguments<'a> {
}
impl<'a> Show for Arguments<'a> {
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
fn fmt(&self, fmt: &mut Formatter) -> Result {
write(fmt.buf, *self)
}
// NOTE(stage0): Remove method after a snapshot
#[cfg(stage0)]
fn fmt(&self, fmt: &mut Formatter) -> Result {
write(fmt.buf, self)
}
@ -268,6 +287,63 @@ static DEFAULT_ARGUMENT: rt::Argument<'static> = rt::Argument {
}
};
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// The `write` function takes an output stream, a precompiled format string,
/// and a list of arguments. The arguments will be formatted according to the
/// specified format string into the output stream provided.
///
/// # Arguments
///
/// * output - the buffer to write output to
/// * args - the precompiled arguments generated by `format_args!`
#[experimental = "libcore and I/O have yet to be reconciled, and this is an \
implementation detail which should not otherwise be exported"]
pub fn write(output: &mut FormatWriter, args: Arguments) -> Result {
let mut formatter = Formatter {
flags: 0,
width: None,
precision: None,
buf: output,
align: rt::AlignUnknown,
fill: ' ',
args: args.args,
curarg: args.args.iter(),
};
let mut pieces = args.pieces.iter();
match args.fmt {
None => {
// We can use default formatting parameters for all arguments.
for _ in range(0, args.args.len()) {
try!(formatter.buf.write(pieces.next().unwrap().as_bytes()));
try!(formatter.run(&DEFAULT_ARGUMENT));
}
}
Some(fmt) => {
// Every spec has a corresponding argument that is preceded by
// a string piece.
for (arg, piece) in fmt.iter().zip(pieces.by_ref()) {
try!(formatter.buf.write(piece.as_bytes()));
try!(formatter.run(arg));
}
}
}
// There can be only one trailing string piece left.
match pieces.next() {
Some(piece) => {
try!(formatter.buf.write(piece.as_bytes()));
}
None => {}
}
Ok(())
}
// NOTE(stage0): Remove function after a snapshot
#[cfg(stage0)]
/// The `write` function takes an output stream, a precompiled format string,
/// and a list of arguments. The arguments will be formatted according to the
/// specified format string into the output stream provided.
@ -527,6 +603,16 @@ impl<'a> Formatter<'a> {
self.buf.write(data)
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// Writes some formatted information into this instance
#[unstable = "reconciling core and I/O may alter this definition"]
pub fn write_fmt(&mut self, fmt: Arguments) -> Result {
write(self.buf, fmt)
}
// NOTE(stage0): Remove method after a snapshot
#[cfg(stage0)]
/// Writes some formatted information into this instance
#[unstable = "reconciling core and I/O may alter this definition"]
pub fn write_fmt(&mut self, fmt: &Arguments) -> Result {

View File

@ -39,7 +39,7 @@
//! distribution.
//!
//! * `rust_begin_unwind` - This function takes three arguments, a
//! `&fmt::Arguments`, a `&str`, and a `uint`. These three arguments dictate
//! `fmt::Arguments`, a `&str`, and a `uint`. These three arguments dictate
//! the panic message, the file at which panic was invoked, and the line.
//! It is up to consumers of this core library to define this panic
//! function; it is only required to never return.

View File

@ -10,6 +10,30 @@
#![macro_escape]
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// Entry point of task panic, for details, see std::macros
#[macro_export]
macro_rules! panic {
() => (
panic!("explicit panic")
);
($msg:expr) => ({
static _MSG_FILE_LINE: (&'static str, &'static str, uint) = ($msg, file!(), line!());
::core::panicking::panic(&_MSG_FILE_LINE)
});
($fmt:expr, $($arg:tt)*) => ({
// The leading _'s are to avoid dead code warnings if this is
// used inside a dead function. Just `#[allow(dead_code)]` is
// insufficient, since the user may have
// `#[forbid(dead_code)]` and which cannot be overridden.
static _FILE_LINE: (&'static str, uint) = (file!(), line!());
::core::panicking::panic_fmt(format_args!($fmt, $($arg)*), &_FILE_LINE)
});
}
// NOTE(stage0): Remove macro after a snapshot
#[cfg(stage0)]
/// Entry point of task panic, for details, see std::macros
#[macro_export]
macro_rules! panic {
@ -105,6 +129,16 @@ macro_rules! try {
($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(e) })
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// Writing a formatted string into a writer
#[macro_export]
macro_rules! write {
($dst:expr, $($arg:tt)*) => ((&mut *$dst).write_fmt(format_args!($($arg)*)))
}
// NOTE(stage0): Remove macro after a snapshot
#[cfg(stage0)]
/// Writing a formatted string into a writer
#[macro_export]
macro_rules! write {

View File

@ -16,7 +16,7 @@
//! interface for panicking is:
//!
//! ```ignore
//! fn panic_impl(fmt: &fmt::Arguments, &(&'static str, uint)) -> !;
//! fn panic_impl(fmt: fmt::Arguments, &(&'static str, uint)) -> !;
//! ```
//!
//! This definition allows for panicking with any general message, but it does not
@ -31,8 +31,20 @@
#![allow(dead_code, missing_docs)]
use fmt;
use intrinsics;
// NOTE(stage0): Remove import after a snapshot
#[cfg(stage0)] use intrinsics;
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
#[cold] #[inline(never)] // this is the slow path, always
#[lang="panic"]
pub fn panic(expr_file_line: &(&'static str, &'static str, uint)) -> ! {
let (expr, file, line) = *expr_file_line;
panic_fmt(format_args!("{}", expr), &(file, line))
}
// NOTE(stage0): Remove function after a snapshot
#[cfg(stage0)]
#[cold] #[inline(never)] // this is the slow path, always
#[lang="panic"]
pub fn panic(expr_file_line: &(&'static str, &'static str, uint)) -> ! {
@ -45,6 +57,18 @@ pub fn panic(expr_file_line: &(&'static str, &'static str, uint)) -> ! {
unsafe { intrinsics::abort() }
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
#[cold] #[inline(never)]
#[lang="panic_bounds_check"]
fn panic_bounds_check(file_line: &(&'static str, uint),
index: uint, len: uint) -> ! {
panic_fmt(format_args!("index out of bounds: the len is {} but the index is {}",
len, index), file_line)
}
// NOTE(stage0): Remove function after a snapshot
#[cfg(stage0)]
#[cold] #[inline(never)]
#[lang="panic_bounds_check"]
fn panic_bounds_check(file_line: &(&'static str, uint),
@ -55,6 +79,21 @@ fn panic_bounds_check(file_line: &(&'static str, uint),
unsafe { intrinsics::abort() }
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
#[cold] #[inline(never)]
pub fn panic_fmt(fmt: fmt::Arguments, file_line: &(&'static str, uint)) -> ! {
#[allow(improper_ctypes)]
extern {
#[lang = "panic_fmt"]
fn panic_impl(fmt: fmt::Arguments, file: &'static str, line: uint) -> !;
}
let (file, line) = *file_line;
unsafe { panic_impl(fmt, file, line) }
}
// NOTE(stage0): Remove function after a snapshot
#[cfg(stage0)]
#[cold] #[inline(never)]
pub fn panic_fmt(fmt: &fmt::Arguments, file_line: &(&'static str, uint)) -> ! {
#[allow(improper_ctypes)]

View File

@ -268,6 +268,8 @@ impl Drop for DefaultLogger {
}
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// This function is called directly by the compiler when using the logging
/// macros. This function does not take into account whether the log level
/// specified is active or not, it will always log something if this method is
@ -276,7 +278,7 @@ impl Drop for DefaultLogger {
/// It is not recommended to call this function directly, rather it should be
/// invoked through the logging family of macros.
#[doc(hidden)]
pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) {
pub fn log(level: u32, loc: &'static LogLocation, args: fmt::Arguments) {
// Test the literal string from args against the current filter, if there
// is one.
match unsafe { FILTER.as_ref() } {
@ -302,6 +304,42 @@ pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) {
set_logger(logger);
}
// NOTE(stage0): Remove function after a snapshot
#[cfg(stage0)]
/// This function is called directly by the compiler when using the logging
/// macros. This function does not take into account whether the log level
/// specified is active or not, it will always log something if this method is
/// called.
///
/// It is not recommended to call this function directly, rather it should be
/// invoked through the logging family of macros.
#[doc(hidden)]
pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) {
// Test the literal string from args against the current filter, if there
// is one.
match unsafe { FILTER.as_ref() } {
Some(filter) if !filter.is_match(args.to_string().as_slice()) => return,
_ => {}
}
// Completely remove the local logger from TLS in case anyone attempts to
// frob the slot while we're doing the logging. This will destroy any logger
// set during logging.
let mut logger = LOCAL_LOGGER.with(|s| {
s.borrow_mut().take()
}).unwrap_or_else(|| {
box DefaultLogger { handle: io::stderr() } as Box<Logger + Send>
});
logger.log(&LogRecord {
level: LogLevel(level),
args: *args,
file: loc.file,
module_path: loc.module_path,
line: loc.line,
});
set_logger(logger);
}
/// Getter for the global log level. This is a function so that it can be called
/// safely
#[doc(hidden)]
@ -329,7 +367,7 @@ pub struct LogRecord<'a> {
pub level: LogLevel,
/// The arguments from the log line.
pub args: &'a fmt::Arguments<'a>,
pub args: fmt::Arguments<'a>,
/// The file of where the LogRecord originated.
pub file: &'a str,

View File

@ -12,6 +12,63 @@
#![macro_escape]
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// The standard logging macro
///
/// This macro will generically log over a provided level (of type u32) with a
/// format!-based argument list. See documentation in `std::fmt` for details on
/// how to use the syntax.
///
/// # Example
///
/// ```
/// #![feature(phase)]
/// #[phase(plugin, link)] extern crate log;
///
/// fn main() {
/// log!(log::WARN, "this is a warning {}", "message");
/// log!(log::DEBUG, "this is a debug message");
/// log!(6, "this is a custom logging level: {level}", level=6u);
/// }
/// ```
///
/// Assumes the binary is `main`:
///
/// ```{.bash}
/// $ RUST_LOG=warn ./main
/// WARN:main: this is a warning message
/// ```
///
/// ```{.bash}
/// $ RUST_LOG=debug ./main
/// DEBUG:main: this is a debug message
/// WARN:main: this is a warning message
/// ```
///
/// ```{.bash}
/// $ RUST_LOG=6 ./main
/// DEBUG:main: this is a debug message
/// WARN:main: this is a warning message
/// 6:main: this is a custom logging level: 6
/// ```
#[macro_export]
macro_rules! log {
($lvl:expr, $($arg:tt)+) => ({
static LOC: ::log::LogLocation = ::log::LogLocation {
line: line!(),
file: file!(),
module_path: module_path!(),
};
let lvl = $lvl;
if log_enabled!(lvl) {
::log::log(lvl, &LOC, format_args!($($arg)+))
}
})
}
// NOTE(stage0): Remove macro after a snapshot
#[cfg(stage0)]
/// The standard logging macro
///
/// This macro will generically log over a provided level (of type u32) with a

View File

@ -16,13 +16,6 @@
//! This macro is implemented in the compiler to emit calls to this module in
//! order to format arguments at runtime into strings and streams.
//!
//! The functions contained in this module should not normally be used in
//! everyday use cases of `format!`. The assumptions made by these functions are
//! unsafe for all inputs, and the compiler performs a large amount of
//! validation on the arguments to `format!` in order to ensure safety at
//! runtime. While it is possible to call these functions directly, it is not
//! recommended to do so in the general case.
//!
//! ## Usage
//!
//! The `format!` macro is intended to be familiar to those coming from C's
@ -275,35 +268,28 @@
//!
//! # #[allow(unused_must_use)]
//! # fn main() {
//! format_args!(fmt::format, "this returns {}", "String");
//! fmt::format(format_args!("this returns {}", "String"));
//!
//! let some_writer: &mut io::Writer = &mut io::stdout();
//! format_args!(|args| { write!(some_writer, "{}", args) },
//! "print with a {}", "closure");
//! write!(some_writer, "{}", format_args!("print with a {}", "macro"));
//!
//! fn my_fmt_fn(args: &fmt::Arguments) {
//! fn my_fmt_fn(args: fmt::Arguments) {
//! write!(&mut io::stdout(), "{}", args);
//! }
//! format_args!(my_fmt_fn, "or a {} too", "function");
//! my_fmt_fn(format_args!("or a {} too", "function"));
//! # }
//! ```
//!
//! The first argument of the `format_args!` macro is a function (or closure)
//! which takes one argument of type `&fmt::Arguments`. This structure can then
//! be passed to the `write` and `format` functions inside this module in order
//! to process the format string. The goal of this macro is to even further
//! prevent intermediate allocations when dealing formatting strings.
//! The result of the `format_args!` macro is a value of type `fmt::Arguments`.
//! This structure can then be passed to the `write` and `format` functions
//! inside this module in order to process the format string.
//! The goal of this macro is to even further prevent intermediate allocations
//! when dealing formatting strings.
//!
//! For example, a logging library could use the standard formatting syntax, but
//! it would internally pass around this structure until it has been determined
//! where output should go to.
//!
//! It is unsafe to programmatically create an instance of `fmt::Arguments`
//! because the operations performed when executing a format string require the
//! compile-time checks provided by the compiler. The `format_args!` macro is
//! the only method of safely creating these structures, but they can be
//! unsafely created with the constructor provided.
//!
//! ## Syntax
//!
//! The syntax for the formatting language used is drawn from other languages,
@ -420,14 +406,39 @@ pub use core::fmt::{Argument, Arguments, write, radix, Radix, RadixFmt};
#[doc(hidden)]
pub use core::fmt::{argument, argumentuint};
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// The format function takes a precompiled format string and a list of
/// arguments, to return the resulting formatted string.
///
/// # Arguments
///
/// * args - a structure of arguments generated via the `format_args!` macro.
///
/// # Example
///
/// ```rust
/// use std::fmt;
///
/// let s = fmt::format(format_args!("Hello, {}!", "world"));
/// assert_eq!(s, "Hello, world!".to_string());
/// ```
#[experimental = "this is an implementation detail of format! and should not \
be called directly"]
pub fn format(args: Arguments) -> string::String {
let mut output = Vec::new();
let _ = write!(&mut output as &mut Writer, "{}", args);
string::String::from_utf8(output).unwrap()
}
// NOTE(stage0): Remove function after a snapshot
#[cfg(stage0)]
/// The format function takes a precompiled format string and a list of
/// arguments, to return the resulting formatted string.
///
/// # Arguments
///
/// * args - a structure of arguments generated via the `format_args!` macro.
/// Because this structure can only be safely generated at
/// compile-time, this function is safe.
///
/// # Example
///

View File

@ -1017,6 +1017,48 @@ pub trait Writer {
/// decide whether their stream needs to be buffered or not.
fn flush(&mut self) -> IoResult<()> { Ok(()) }
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// Writes a formatted string into this writer, returning any error
/// encountered.
///
/// This method is primarily used to interface with the `format_args!`
/// macro, but it is rare that this should explicitly be called. The
/// `write!` macro should be favored to invoke this method instead.
///
/// # Errors
///
/// This function will return any I/O error reported while formatting.
fn write_fmt(&mut self, fmt: fmt::Arguments) -> IoResult<()> {
// Create a shim which translates a Writer to a FormatWriter and saves
// off I/O errors. instead of discarding them
struct Adaptor<'a, T:'a> {
inner: &'a mut T,
error: IoResult<()>,
}
impl<'a, T: Writer> fmt::FormatWriter for Adaptor<'a, T> {
fn write(&mut self, bytes: &[u8]) -> fmt::Result {
match self.inner.write(bytes) {
Ok(()) => Ok(()),
Err(e) => {
self.error = Err(e);
Err(fmt::Error)
}
}
}
}
let mut output = Adaptor { inner: self, error: Ok(()) };
match fmt::write(&mut output, fmt) {
Ok(()) => Ok(()),
Err(..) => output.error
}
}
// NOTE(stage0): Remove method after a snapshot
#[cfg(stage0)]
/// Writes a formatted string into this writer, returning any error
/// encountered.
///

View File

@ -378,12 +378,32 @@ pub fn println(s: &str) {
})
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
/// with the `format_args!` macro.
pub fn print_args(fmt: fmt::Arguments) {
with_task_stdout(|io| write!(io, "{}", fmt))
}
// NOTE(stage0): Remove function after a snapshot
#[cfg(stage0)]
/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
/// with the `format_args!` macro.
pub fn print_args(fmt: &fmt::Arguments) {
with_task_stdout(|io| write!(io, "{}", fmt))
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// Similar to `println`, but takes a `fmt::Arguments` structure to be
/// compatible with the `format_args!` macro.
pub fn println_args(fmt: fmt::Arguments) {
with_task_stdout(|io| writeln!(io, "{}", fmt))
}
// NOTE(stage0): Remove function after a snapshot
#[cfg(stage0)]
/// Similar to `println`, but takes a `fmt::Arguments` structure to be
/// compatible with the `format_args!` macro.
pub fn println_args(fmt: &fmt::Arguments) {

View File

@ -17,6 +17,50 @@
#![experimental]
#![macro_escape]
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// The entry point for panic of Rust tasks.
///
/// This macro is used to inject panic into a Rust task, causing the task to
/// unwind and panic entirely. Each task's panic can be reaped as the
/// `Box<Any>` type, and the single-argument form of the `panic!` macro will be
/// the value which is transmitted.
///
/// The multi-argument form of this macro panics with a string and has the
/// `format!` syntax for building a string.
///
/// # Example
///
/// ```should_fail
/// # #![allow(unreachable_code)]
/// panic!();
/// panic!("this is a terrible mistake!");
/// panic!(4i); // panic with the value of 4 to be collected elsewhere
/// panic!("this is a {} {message}", "fancy", message = "message");
/// ```
#[macro_export]
macro_rules! panic {
() => ({
panic!("explicit panic")
});
($msg:expr) => ({
// static requires less code at runtime, more constant data
static _FILE_LINE: (&'static str, uint) = (file!(), line!());
::std::rt::begin_unwind($msg, &_FILE_LINE)
});
($fmt:expr, $($arg:tt)*) => ({
// The leading _'s are to avoid dead code warnings if this is
// used inside a dead function. Just `#[allow(dead_code)]` is
// insufficient, since the user may have
// `#[forbid(dead_code)]` and which cannot be overridden.
static _FILE_LINE: (&'static str, uint) = (file!(), line!());
::std::rt::begin_unwind_fmt(format_args!($fmt, $($arg)*), &_FILE_LINE)
});
}
// NOTE(stage0): Remove macro after a snapshot
#[cfg(stage0)]
/// The entry point for panic of Rust tasks.
///
/// This macro is used to inject panic into a Rust task, causing the task to
@ -245,6 +289,26 @@ macro_rules! unimplemented {
() => (panic!("not yet implemented"))
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// Use the syntax described in `std::fmt` to create a value of type `String`.
/// See `std::fmt` for more information.
///
/// # Example
///
/// ```
/// format!("test");
/// format!("hello {}", "world!");
/// format!("x = {}, y = {y}", 10i, y = 30i);
/// ```
#[macro_export]
#[stable]
macro_rules! format {
($($arg:tt)*) => (::std::fmt::format(format_args!($($arg)*)))
}
// NOTE(stage0): Remove macro after a snapshot
#[cfg(stage0)]
/// Use the syntax described in `std::fmt` to create a value of type `String`.
/// See `std::fmt` for more information.
///
@ -263,6 +327,28 @@ macro_rules! format {
)
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// Use the `format!` syntax to write data into a buffer of type `&mut Writer`.
/// See `std::fmt` for more information.
///
/// # Example
///
/// ```
/// # #![allow(unused_must_use)]
///
/// let mut w = Vec::new();
/// write!(&mut w, "test");
/// write!(&mut w, "formatted {}", "arguments");
/// ```
#[macro_export]
#[stable]
macro_rules! write {
($dst:expr, $($arg:tt)*) => ((&mut *$dst).write_fmt(format_args!($($arg)*)))
}
// NOTE(stage0): Remove macro after a snapshot
#[cfg(stage0)]
/// Use the `format!` syntax to write data into a buffer of type `&mut Writer`.
/// See `std::fmt` for more information.
///
@ -294,6 +380,18 @@ macro_rules! writeln {
)
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// Equivalent to the `println!` macro except that a newline is not printed at
/// the end of the message.
#[macro_export]
#[stable]
macro_rules! print {
($($arg:tt)*) => (::std::io::stdio::print_args(format_args!($($arg)*)))
}
// NOTE(stage0): Remove macro after a snapshot
#[cfg(stage0)]
/// Equivalent to the `println!` macro except that a newline is not printed at
/// the end of the message.
#[macro_export]
@ -302,6 +400,28 @@ macro_rules! print {
($($arg:tt)*) => (format_args!(::std::io::stdio::print_args, $($arg)*))
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// Macro for printing to a task's stdout handle.
///
/// Each task can override its stdout handle via `std::io::stdio::set_stdout`.
/// The syntax of this macro is the same as that used for `format!`. For more
/// information, see `std::fmt` and `std::io::stdio`.
///
/// # Example
///
/// ```
/// println!("hello there!");
/// println!("format {} arguments", "some");
/// ```
#[macro_export]
#[stable]
macro_rules! println {
($($arg:tt)*) => (::std::io::stdio::println_args(format_args!($($arg)*)))
}
// NOTE(stage0): Remove macro after a snapshot
#[cfg(stage0)]
/// Macro for printing to a task's stdout handle.
///
/// Each task can override its stdout handle via `std::io::stdio::set_stdout`.
@ -411,11 +531,10 @@ macro_rules! log {
pub mod builtin {
/// The core macro for formatted string creation & output.
///
/// This macro takes as its first argument a callable expression which will
/// receive as its first argument a value of type `&fmt::Arguments`. This
/// value can be passed to the functions in `std::fmt` for performing useful
/// functions. All other formatting macros (`format!`, `write!`,
/// `println!`, etc) are proxied through this one.
/// This macro produces a value of type `fmt::Arguments`. This value can be
/// passed to the functions in `std::fmt` for performing useful functions.
/// All other formatting macros (`format!`, `write!`, `println!`, etc) are
/// proxied through this one.
///
/// For more information, see the documentation in `std::fmt`.
///
@ -424,15 +543,12 @@ pub mod builtin {
/// ```rust
/// use std::fmt;
///
/// let s = format_args!(fmt::format, "hello {}", "world");
/// let s = fmt::format(format_args!("hello {}", "world"));
/// assert_eq!(s, format!("hello {}", "world"));
///
/// format_args!(|args| {
/// // pass `args` to another function, etc.
/// }, "hello {}", "world");
/// ```
#[macro_export]
macro_rules! format_args { ($closure:expr, $fmt:expr $($args:tt)*) => ({
macro_rules! format_args { ($fmt:expr $($args:tt)*) => ({
/* compiler built-in */
}) }

View File

@ -15,6 +15,16 @@
#![macro_escape]
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
macro_rules! rterrln {
($fmt:expr $($arg:tt)*) => ( {
::rt::util::dumb_print(format_args!(concat!($fmt, "\n") $($arg)*))
} )
}
// NOTE(stage0): Remove macro after a snapshot
#[cfg(stage0)]
macro_rules! rterrln {
($fmt:expr $($arg:tt)*) => ( {
format_args!(::rt::util::dumb_print, concat!($fmt, "\n") $($arg)*)
@ -40,6 +50,14 @@ macro_rules! rtassert {
} )
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
macro_rules! rtabort {
($($arg:tt)*) => (::rt::util::abort(format_args!($($arg)*)))
}
// NOTE(stage0): Remove macro after a snapshot
#[cfg(stage0)]
macro_rules! rtabort {
($($arg:tt)*) => (format_args!(::rt::util::abort, $($arg)*))
}

View File

@ -477,14 +477,61 @@ pub mod eabi {
}
}
// Entry point of panic from the libcore crate
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
#[cfg(not(test))]
/// Entry point of panic from the libcore crate.
#[lang = "panic_fmt"]
pub extern fn rust_begin_unwind(msg: fmt::Arguments,
file: &'static str, line: uint) -> ! {
begin_unwind_fmt(msg, &(file, line))
}
// NOTE(stage0): Remove function after a snapshot
#[cfg(stage0)]
#[cfg(not(test))]
/// Entry point of panic from the libcore crate.
#[lang = "panic_fmt"]
pub extern fn rust_begin_unwind(msg: &fmt::Arguments,
file: &'static str, line: uint) -> ! {
begin_unwind_fmt(msg, &(file, line))
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
/// The entry point for unwinding with a formatted message.
///
/// This is designed to reduce the amount of code required at the call
/// site as much as possible (so that `panic!()` has as low an impact
/// on (e.g.) the inlining of other functions as possible), by moving
/// the actual formatting into this shared place.
#[inline(never)] #[cold]
pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, uint)) -> ! {
use fmt::FormatWriter;
// We do two allocations here, unfortunately. But (a) they're
// required with the current scheme, and (b) we don't handle
// panic + OOM properly anyway (see comment in begin_unwind
// below).
struct VecWriter<'a> { v: &'a mut Vec<u8> }
impl<'a> fmt::FormatWriter for VecWriter<'a> {
fn write(&mut self, buf: &[u8]) -> fmt::Result {
self.v.push_all(buf);
Ok(())
}
}
let mut v = Vec::new();
let _ = write!(&mut VecWriter { v: &mut v }, "{}", msg);
let msg = box String::from_utf8_lossy(v.as_slice()).into_owned();
begin_unwind_inner(msg, file_line)
}
// NOTE(stage0): Remove function after a snapshot
#[cfg(stage0)]
/// The entry point for unwinding with a formatted message.
///
/// This is designed to reduce the amount of code required at the call

View File

@ -112,12 +112,25 @@ impl fmt::FormatWriter for Stdio {
}
}
// NOTE(stage0): Remove cfg after a snapshot
#[cfg(not(stage0))]
pub fn dumb_print(args: fmt::Arguments) {
let _ = Stderr.write_fmt(args);
}
// NOTE(stage0): Remove function after a snapshot
#[cfg(stage0)]
pub fn dumb_print(args: &fmt::Arguments) {
let mut w = Stderr;
let _ = write!(&mut w, "{}", args);
}
pub fn abort(args: &fmt::Arguments) -> ! {
// NOTE(stage0): Remove wrappers after a snapshot
#[cfg(not(stage0))] pub fn abort(args: fmt::Arguments) -> ! { abort_(&args) }
#[cfg(stage0)] pub fn abort(args: &fmt::Arguments) -> ! { abort_(args) }
// NOTE(stage0): Change to `pub fn abort(args: fmt::Arguments) -> !` after a snapshot
fn abort_(args: &fmt::Arguments) -> ! {
use fmt::FormatWriter;
struct BufWriter<'a> {

View File

@ -131,8 +131,10 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span,
let format_string = cx.expr_str(span, s);
// phew, not our responsibility any more!
format::expand_preparsed_format_args(cx, span,
format::MethodCall(formatter, meth),
format_string, exprs, Vec::new(),
HashMap::new())
let args = vec![
format::expand_preparsed_format_args(cx, span, format_string,
exprs, vec![], HashMap::new())
];
cx.expr_method_call(span, formatter, meth, args)
}

View File

@ -8,7 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub use self::Invocation::*;
use self::ArgumentType::*;
use self::Position::*;
@ -23,17 +22,16 @@ use parse::token;
use ptr::P;
use std::collections::HashMap;
use std::string;
#[deriving(PartialEq)]
enum ArgumentType {
Known(string::String),
Known(String),
Unsigned
}
enum Position {
Exact(uint),
Named(string::String),
Named(String),
}
struct Context<'a, 'b:'a> {
@ -48,12 +46,12 @@ struct Context<'a, 'b:'a> {
/// Note that we keep a side-array of the ordering of the named arguments
/// found to be sure that we can translate them in the same order that they
/// were declared in.
names: HashMap<string::String, P<ast::Expr>>,
name_types: HashMap<string::String, ArgumentType>,
name_ordering: Vec<string::String>,
names: HashMap<String, P<ast::Expr>>,
name_types: HashMap<String, ArgumentType>,
name_ordering: Vec<String>,
/// The latest consecutive literal strings, or empty if there weren't any.
literal: string::String,
literal: String,
/// Collection of the compiled `rt::Argument` structures
pieces: Vec<P<ast::Expr>>,
@ -62,62 +60,40 @@ struct Context<'a, 'b:'a> {
/// Stays `true` if all formatting parameters are default (as in "{}{}").
all_pieces_simple: bool,
name_positions: HashMap<string::String, uint>,
method_statics: Vec<P<ast::Item>>,
name_positions: HashMap<String, uint>,
/// Updated as arguments are consumed or methods are entered
nest_level: uint,
next_arg: uint,
}
pub enum Invocation {
Call(P<ast::Expr>),
MethodCall(P<ast::Expr>, ast::Ident),
}
/// Parses the arguments from the given list of tokens, returning None
/// if there's a parse error so we can continue parsing other format!
/// expressions.
///
/// If parsing succeeds, the second return value is:
/// If parsing succeeds, the return value is:
///
/// Some((fmtstr, unnamed arguments, ordering of named arguments,
/// named arguments))
fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool,
tts: &[ast::TokenTree])
-> (Invocation, Option<(P<ast::Expr>, Vec<P<ast::Expr>>, Vec<string::String>,
HashMap<string::String, P<ast::Expr>>)>) {
fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> Option<(P<ast::Expr>, Vec<P<ast::Expr>>, Vec<String>,
HashMap<String, P<ast::Expr>>)> {
let mut args = Vec::new();
let mut names = HashMap::<string::String, P<ast::Expr>>::new();
let mut names = HashMap::<String, P<ast::Expr>>::new();
let mut order = Vec::new();
let mut p = ecx.new_parser_from_tts(tts);
// Parse the leading function expression (maybe a block, maybe a path)
let invocation = if allow_method {
let e = p.parse_expr();
if !p.eat(&token::Comma) {
ecx.span_err(sp, "expected token: `,`");
return (Call(e), None);
}
MethodCall(e, p.parse_ident())
} else {
Call(p.parse_expr())
};
if !p.eat(&token::Comma) {
ecx.span_err(sp, "expected token: `,`");
return (invocation, None);
}
if p.token == token::Eof {
ecx.span_err(sp, "requires at least a format string argument");
return (invocation, None);
return None;
}
let fmtstr = p.parse_expr();
let mut named = false;
while p.token != token::Eof {
if !p.eat(&token::Comma) {
ecx.span_err(sp, "expected token: `,`");
return (invocation, None);
return None;
}
if p.token == token::Eof { break } // accept trailing commas
if named || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) {
@ -131,13 +107,13 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool,
ecx.span_err(p.span,
"expected ident, positional arguments \
cannot follow named arguments");
return (invocation, None);
return None;
}
_ => {
ecx.span_err(p.span,
format!("expected ident for named argument, found `{}`",
p.this_token_to_string())[]);
return (invocation, None);
return None;
}
};
let interned_name = token::get_ident(ident);
@ -160,7 +136,7 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool,
args.push(p.parse_expr());
}
}
return (invocation, Some((fmtstr, args, order, names)));
Some((fmtstr, args, order, names))
}
impl<'a, 'b> Context<'a, 'b> {
@ -225,7 +201,7 @@ impl<'a, 'b> Context<'a, 'b> {
}
}
fn describe_num_args(&self) -> string::String {
fn describe_num_args(&self) -> String {
match self.args.len() {
0 => "no arguments given".to_string(),
1 => "there is 1 argument".to_string(),
@ -474,70 +450,50 @@ impl<'a, 'b> Context<'a, 'b> {
}
}
fn item_static_array(ecx: &mut ExtCtxt,
name: ast::Ident,
piece_ty: P<ast::Ty>,
pieces: Vec<P<ast::Expr>>)
-> P<ast::Stmt> {
fn static_array(ecx: &mut ExtCtxt,
name: &str,
piece_ty: P<ast::Ty>,
pieces: Vec<P<ast::Expr>>)
-> P<ast::Expr> {
let fmtsp = piece_ty.span;
let fmt = ecx.expr_vec(fmtsp, pieces);
let fmt = ecx.expr_addr_of(fmtsp, fmt);
let ty = ast::TyVec(piece_ty);
let ty = ast::TyRptr(Some(ecx.lifetime(fmtsp, special_idents::static_lifetime.name)),
ast::MutTy{ mutbl: ast::MutImmutable, ty: ecx.ty(fmtsp, ty) });
let ty = ecx.ty(fmtsp, ty);
let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
let ty = ecx.ty_rptr(fmtsp,
ecx.ty(fmtsp, ast::TyVec(piece_ty)),
Some(ecx.lifetime(fmtsp, special_idents::static_lifetime.name)),
ast::MutImmutable);
let slice = ecx.expr_vec_slice(fmtsp, pieces);
let st = ast::ItemStatic(ty, ast::MutImmutable, slice);
let name = ecx.ident_of(name);
let item = ecx.item(fmtsp, name, Context::static_attrs(ecx, fmtsp), st);
let decl = respan(fmtsp, ast::DeclItem(item));
P(respan(fmtsp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
// Wrap the declaration in a block so that it forms a single expression.
ecx.expr_block(ecx.block(fmtsp,
vec![P(respan(fmtsp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))],
Some(ecx.expr_ident(fmtsp, name))))
}
/// Actually builds the expression which the iformat! block will be expanded
/// to
fn to_expr(mut self, invocation: Invocation) -> P<ast::Expr> {
let mut lets = Vec::new();
fn into_expr(mut self) -> P<ast::Expr> {
let mut locals = Vec::new();
let mut names = Vec::from_fn(self.name_positions.len(), |_| None);
let mut pats = Vec::new();
let mut heads = Vec::new();
// First, declare all of our methods that are statics
for method in self.method_statics.into_iter() {
let decl = respan(self.fmtsp, ast::DeclItem(method));
lets.push(P(respan(self.fmtsp,
ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))));
}
// Next, build up the static array which will become our precompiled
// First, build up the static array which will become our precompiled
// format "string"
let static_str_name = self.ecx.ident_of("__STATIC_FMTSTR");
let static_lifetime = self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("'static").name);
let static_lifetime = self.ecx.lifetime(self.fmtsp, special_idents::static_lifetime.name);
let piece_ty = self.ecx.ty_rptr(
self.fmtsp,
self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
Some(static_lifetime),
ast::MutImmutable);
lets.push(Context::item_static_array(self.ecx,
static_str_name,
piece_ty,
self.str_pieces));
let pieces = Context::static_array(self.ecx,
"__STATIC_FMTSTR",
piece_ty,
self.str_pieces);
// Then, build up the static array which will store our precompiled
// nonstandard placeholders, if there are any.
let static_args_name = self.ecx.ident_of("__STATIC_FMTARGS");
if !self.all_pieces_simple {
let piece_ty = self.ecx.ty_path(self.ecx.path_all(
self.fmtsp,
true, Context::rtpath(self.ecx, "Argument"),
vec![static_lifetime],
vec![],
vec![]
));
lets.push(Context::item_static_array(self.ecx,
static_args_name,
piece_ty,
self.pieces));
}
// Right now there is a bug such that for the expression:
// foo(bar(&1))
@ -580,71 +536,68 @@ impl<'a, 'b> Context<'a, 'b> {
// Now create a vector containing all the arguments
let args = locals.into_iter().chain(names.into_iter().map(|a| a.unwrap()));
// Now create the fmt::Arguments struct with all our locals we created.
let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name);
let args_slice = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
let (fn_name, fn_args) = if self.all_pieces_simple {
("new", vec![pieces, args_slice])
} else {
let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
("with_placeholders", vec![pieces, fmt, args_slice])
};
let result = self.ecx.expr_call_global(self.fmtsp, vec!(
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("Arguments"),
self.ecx.ident_of(fn_name)), fn_args);
let result = match invocation {
Call(e) => {
let span = e.span;
self.ecx.expr_call(span, e, vec![
self.ecx.expr_addr_of(span, result)
])
}
MethodCall(e, m) => {
let span = e.span;
self.ecx.expr_method_call(span, e, m, vec![
self.ecx.expr_addr_of(span, result)
])
}
};
let body = self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
Some(result)));
let args_array = self.ecx.expr_vec(self.fmtsp, args.collect());
// Constructs an AST equivalent to:
//
// match (&arg0, &arg1) {
// (tmp0, tmp1) => body
// (tmp0, tmp1) => args_array
// }
//
// It was:
//
// let tmp0 = &arg0;
// let tmp1 = &arg1;
// body
// args_array
//
// Because of #11585 the new temporary lifetime rule, the enclosing
// statements for these temporaries become the let's themselves.
// If one or more of them are RefCell's, RefCell borrow() will also
// end there; they don't last long enough for body to use them. The
// match expression solves the scope problem.
// end there; they don't last long enough for args_array to use them.
// The match expression solves the scope problem.
//
// Note, it may also very well be transformed to:
//
// match arg0 {
// ref tmp0 => {
// match arg1 => {
// ref tmp1 => body } } }
// ref tmp1 => args_array } } }
//
// But the nested match expression is proved to perform not as well
// as series of let's; the first approach does.
let pat = self.ecx.pat_tuple(self.fmtsp, pats);
let arm = self.ecx.arm(self.fmtsp, vec!(pat), body);
let arm = self.ecx.arm(self.fmtsp, vec!(pat), args_array);
let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads));
self.ecx.expr_match(self.fmtsp, head, vec!(arm))
let result = self.ecx.expr_match(self.fmtsp, head, vec!(arm));
let args_slice = self.ecx.expr_addr_of(self.fmtsp, result);
// Now create the fmt::Arguments struct with all our locals we created.
let (fn_name, fn_args) = if self.all_pieces_simple {
("new", vec![pieces, args_slice])
} else {
// Build up the static array which will store our precompiled
// nonstandard placeholders, if there are any.
let piece_ty = self.ecx.ty_path(self.ecx.path_all(
self.fmtsp,
true, Context::rtpath(self.ecx, "Argument"),
vec![static_lifetime],
vec![],
vec![]
));
let fmt = Context::static_array(self.ecx,
"__STATIC_FMTARGS",
piece_ty,
self.pieces);
("with_placeholders", vec![pieces, fmt, args_slice])
};
self.ecx.expr_call_global(self.fmtsp, vec!(
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("Arguments"),
self.ecx.ident_of(fn_name)), fn_args)
}
fn format_arg(ecx: &ExtCtxt, sp: Span,
@ -694,24 +647,22 @@ pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
tts: &[ast::TokenTree])
-> Box<base::MacResult+'cx> {
match parse_args(ecx, sp, false, tts) {
(invocation, Some((efmt, args, order, names))) => {
MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
match parse_args(ecx, sp, tts) {
Some((efmt, args, order, names)) => {
MacExpr::new(expand_preparsed_format_args(ecx, sp, efmt,
args, order, names))
}
(_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
None => DummyResult::expr(sp)
}
}
/// Take the various parts of `format_args!(extra, efmt, args...,
/// name=names...)` and construct the appropriate formatting
/// expression.
/// Take the various parts of `format_args!(efmt, args..., name=names...)`
/// and construct the appropriate formatting expression.
pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
invocation: Invocation,
efmt: P<ast::Expr>,
args: Vec<P<ast::Expr>>,
name_ordering: Vec<string::String>,
names: HashMap<string::String, P<ast::Expr>>)
name_ordering: Vec<String>,
names: HashMap<String, P<ast::Expr>>)
-> P<ast::Expr> {
let arg_types = Vec::from_fn(args.len(), |_| None);
let mut cx = Context {
@ -724,17 +675,16 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
name_ordering: name_ordering,
nest_level: 0,
next_arg: 0,
literal: string::String::new(),
literal: String::new(),
pieces: Vec::new(),
str_pieces: Vec::new(),
all_pieces_simple: true,
method_statics: Vec::new(),
fmtsp: sp,
};
cx.fmtsp = efmt.span;
let fmt = match expr_to_string(cx.ecx,
efmt,
"format argument must be a string literal.") {
efmt,
"format argument must be a string literal.") {
Some((fmt, _)) => fmt,
None => return DummyResult::raw_expr(sp)
};
@ -782,5 +732,5 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
}
}
cx.to_expr(invocation)
cx.into_expr()
}

View File

@ -13,5 +13,5 @@
fn main() {
let x: || -> ! = || panic!();
x();
println!("Foo bar"); //~ ERROR: unreachable statement
std::io::println("Foo bar"); //~ ERROR: unreachable statement
}

View File

@ -1,4 +1,4 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@ -9,6 +9,6 @@
// except according to those terms.
fn main() {
format_args!("test"); //~ ERROR: expected token
format_args!("", || {}); //~ ERROR: must be a string literal
format_args!(); //~ ERROR: requires at least a format string argument
format_args!(|| {}); //~ ERROR: must be a string literal
}

View File

@ -1,13 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main() {
format_args!("{}", ""); //~ ERROR: expected function
}

View File

@ -34,13 +34,6 @@ pub fn bar() {
[int; 3]) as &[int; 3]) as *const _ as *const [int; 3]) as
*const [int; (3u as uint)] as *const [int; 3]);
(match (() as ()) {
() => {
#[inline]
#[allow(dead_code)]
static __STATIC_FMTSTR: &'static [&'static str] =
(&([("test" as &'static str)] as [&'static str; 1]) as
&'static [&'static str; 1]);
@ -49,24 +42,45 @@ pub fn bar() {
((::std::fmt::format as
fn(&core::fmt::Arguments<'_>) -> collections::string::String {std::fmt::format})((&((::std::fmt::Arguments::new
as
fn(&[&str], &[core::fmt::Argument<'_>]) -> core::fmt::Arguments<'_> {core::fmt::Arguments<'a>::new})((__STATIC_FMTSTR
as
&'static [&'static str]),
(&([]
as
[core::fmt::Argument<'_>; 0])
as
&[core::fmt::Argument<'_>; 0]))
as
core::fmt::Arguments<'_>)
as
&core::fmt::Arguments<'_>))
as collections::string::String)
}
} as collections::string::String);
((::std::fmt::format as
fn(core::fmt::Arguments<'_>) -> collections::string::String {std::fmt::format})(((::std::fmt::Arguments::new
as
fn(&[&str], &[core::fmt::Argument<'_>]) -> core::fmt::Arguments<'_> {core::fmt::Arguments<'a>::new})(({
#[inline]
#[allow(dead_code)]
static __STATIC_FMTSTR:
&'static [&'static str]
=
(&([("test"
as
&'static str)]
as
[&'static str; 1])
as
&'static [&'static str; 1]);
(__STATIC_FMTSTR
as
&'static [&'static str])
}
as
&[&str]),
(&(match (()
as
())
{
()
=>
([]
as
[core::fmt::Argument<'_>; 0]),
}
as
[core::fmt::Argument<'_>; 0])
as
&[core::fmt::Argument<'_>; 0]))
as
core::fmt::Arguments<'_>))
as collections::string::String);
}
pub type Foo = [int; (3u as uint)];
pub struct Bar {

View File

@ -190,18 +190,16 @@ fn test_format_args() {
let mut buf = Vec::new();
{
let w = &mut buf as &mut io::Writer;
format_args!(|args| { write!(w, "{}", args); }, "{}", 1i);
format_args!(|args| { write!(w, "{}", args); }, "test");
format_args!(|args| { write!(w, "{}", args); }, "{test}", test=3i);
write!(w, "{}", format_args!("{}", 1i));
write!(w, "{}", format_args!("test"));
write!(w, "{}", format_args!("{test}", test=3i));
}
let s = String::from_utf8(buf).unwrap();
t!(s, "1test3");
let s = format_args!(fmt::format, "hello {}", "world");
let s = fmt::format(format_args!("hello {}", "world"));
t!(s, "hello world");
let s = format_args!(|args| {
format!("{}: {}", "args were", args)
}, "hello {}", "world");
let s = format!("{}: {}", "args were", format_args!("hello {}", "world"));
t!(s, "args were: hello world");
}