Skip to content

Commit 4ecb0a3

Browse files
committed
auto merge of #9153 : alexcrichton/rust/simplify-format, r=huonw
This follows from the discussion in #9012. * All macros are now defined in terms of `format_args!` allowing for removal of a good bit of code in the syntax extension * The syntax extension is now in a more aptly-named file, `format.rs` * Documentation was added for the `format!`-related macros.
2 parents 36872e4 + 6406138 commit 4ecb0a3

File tree

6 files changed

+172
-142
lines changed

6 files changed

+172
-142
lines changed

src/libstd/fmt/mod.rs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ is `?` which is defined for all types by default.
133133
When implementing a format trait for your own time, you will have to implement a
134134
method of the signature:
135135
136-
~~~
136+
~~~{.rust}
137137
fn fmt(value: &T, f: &mut std::fmt::Formatter);
138138
~~~
139139
@@ -144,6 +144,78 @@ values of these parameters will be listed in the fields of the `Formatter`
144144
struct. In order to help with this, the `Formatter` struct also provides some
145145
helper methods.
146146
147+
### Related macros
148+
149+
There are a number of related macros in the `format!` family. The ones that are
150+
currently implemented are:
151+
152+
~~~{.rust}
153+
format! // described above
154+
write! // first argument is a &mut rt::io::Writer, the destination
155+
writeln! // same as write but appends a newline
156+
print! // the format string is printed to the standard output
157+
println! // same as print but appends a newline
158+
format_args! // described below.
159+
~~~
160+
161+
162+
#### `write!`
163+
164+
This and `writeln` are two macros which are used to emit the format string to a
165+
specified stream. This is used to prevent intermediate allocations of format
166+
strings and instead directly write the output. Under the hood, this function is
167+
actually invoking the `write` function defined in this module. Example usage is:
168+
169+
~~~{.rust}
170+
use std::rt::io;
171+
172+
let mut w = io::mem::MemWriter::new();
173+
write!(&mut w as &mut io::Writer, "Hello {}!", "world");
174+
~~~
175+
176+
#### `print!`
177+
178+
This and `println` emit their output to stdout. Similarly to the `write!` macro,
179+
the goal of these macros is to avoid intermediate allocations when printing
180+
output. Example usage is:
181+
182+
~~~{.rust}
183+
print!("Hello {}!", "world");
184+
println!("I have a newline {}", "character at the end");
185+
~~~
186+
187+
#### `format_args!`
188+
This is a curious macro which is used to safely pass around
189+
an opaque object describing the format string. This object
190+
does not require any heap allocations to create, and it only
191+
references information on the stack. Under the hood, all of
192+
the related macros are implemented in terms of this. First
193+
off, some example usage is:
194+
195+
~~~{.rust}
196+
use std::fmt;
197+
198+
format_args!(fmt::format, "this returns {}", "~str");
199+
format_args!(|args| { fmt::write(my_writer, args) }, "some {}", "args");
200+
format_args!(my_fn, "format {}", "string");
201+
~~~
202+
203+
The first argument of the `format_args!` macro is a function (or closure) which
204+
takes one argument of type `&fmt::Arguments`. This structure can then be
205+
passed to the `write` and `format` functions inside this module in order to
206+
process the format string. The goal of this macro is to even further prevent
207+
intermediate allocations when dealing formatting strings.
208+
209+
For example, a logging library could use the standard formatting syntax, but it
210+
would internally pass around this structure until it has been determined where
211+
output should go to.
212+
213+
It is unsafe to programmatically create an instance of `fmt::Arguments` because
214+
the operations performed when executing a format string require the compile-time
215+
checks provided by the compiler. The `format_args!` macro is the only method of
216+
safely creating these structures, but they can be unsafely created with the
217+
constructor provided.
218+
147219
## Internationalization
148220
149221
The formatting syntax supported by the `format!` extension supports
@@ -163,7 +235,7 @@ Furthermore, whenever a case is running, the special character `#` can be used
163235
to reference the string value of the argument which was selected upon. As an
164236
example:
165237
166-
~~~
238+
~~~{.rust}
167239
format!("{0, select, other{#}}", "hello") // => ~"hello"
168240
~~~
169241
@@ -452,6 +524,13 @@ pub fn write(output: &mut io::Writer, args: &Arguments) {
452524
unsafe { write_unsafe(output, args.fmt, args.args) }
453525
}
454526

527+
/// The `writeln` function takes the same arguments as `write`, except that it
528+
/// will also write a newline (`\n`) character at the end of the format string.
529+
pub fn writeln(output: &mut io::Writer, args: &Arguments) {
530+
unsafe { write_unsafe(output, args.fmt, args.args) }
531+
output.write(['\n' as u8]);
532+
}
533+
455534
/// The `write_unsafe` function takes an output stream, a precompiled format
456535
/// string, and a list of arguments. The arguments will be formatted according
457536
/// to the specified format string into the output stream provided.

src/libsyntax/ext/base.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,8 @@ pub fn syntax_expander_table() -> SyntaxEnv {
155155
@SE(IdentTT(ext::tt::macro_rules::add_new_extension, None)));
156156
syntax_expanders.insert(intern(&"fmt"),
157157
builtin_normal_tt_no_ctxt(ext::fmt::expand_syntax_ext));
158-
syntax_expanders.insert(intern(&"format"),
159-
builtin_normal_tt_no_ctxt(ext::ifmt::expand_format));
160-
syntax_expanders.insert(intern(&"write"),
161-
builtin_normal_tt_no_ctxt(ext::ifmt::expand_write));
162-
syntax_expanders.insert(intern(&"writeln"),
163-
builtin_normal_tt_no_ctxt(ext::ifmt::expand_writeln));
164158
syntax_expanders.insert(intern(&"format_args"),
165-
builtin_normal_tt_no_ctxt(ext::ifmt::expand_format_args));
159+
builtin_normal_tt_no_ctxt(ext::format::expand_args));
166160
syntax_expanders.insert(
167161
intern(&"auto_encode"),
168162
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));

src/libsyntax/ext/expand.rs

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,10 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
8181
// be the root of the call stack. That's the most
8282
// relevant span and it's the actual invocation of
8383
// the macro.
84-
let mut relevant_info = cx.backtrace();
85-
let mut einfo = relevant_info.unwrap();
86-
loop {
87-
match relevant_info {
88-
None => { break }
89-
Some(e) => {
90-
einfo = e;
91-
relevant_info = einfo.call_site.expn_info;
92-
}
93-
}
94-
}
84+
let mac_span = original_span(cx);
9585

9686
let expanded =
97-
match expandfun(cx, einfo.call_site,
87+
match expandfun(cx, mac_span.call_site,
9888
marked_before, marked_ctxt) {
9989
MRExpr(e) => e,
10090
MRAny(expr_maker,_,_) => expr_maker(),
@@ -400,11 +390,11 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
400390
-> (Option<Stmt_>, Span) {
401391
// why the copying here and not in expand_expr?
402392
// looks like classic changed-in-only-one-place
403-
let (mac, pth, tts, semi, ctxt) = match *s {
393+
let (pth, tts, semi, ctxt) = match *s {
404394
StmtMac(ref mac, semi) => {
405395
match mac.node {
406396
mac_invoc_tt(ref pth, ref tts, ctxt) => {
407-
((*mac).clone(), pth, (*tts).clone(), semi, ctxt)
397+
(pth, (*tts).clone(), semi, ctxt)
408398
}
409399
}
410400
}
@@ -431,7 +421,13 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
431421
// mark before expansion:
432422
let marked_tts = mark_tts(tts,fm);
433423
let marked_ctxt = new_mark(fm,ctxt);
434-
let expanded = match expandfun(cx, mac.span, marked_tts, marked_ctxt) {
424+
425+
// See the comment in expand_expr for why we want the original span,
426+
// not the current mac.span.
427+
let mac_span = original_span(cx);
428+
429+
let expanded = match expandfun(cx, mac_span.call_site,
430+
marked_tts, marked_ctxt) {
435431
MRExpr(e) =>
436432
@codemap::Spanned { node: StmtExpr(e, ast::DUMMY_NODE_ID),
437433
span: e.span},
@@ -715,11 +711,11 @@ pub fn std_macros() -> @str {
715711
}
716712
})
717713
)
718-
macro_rules! error( ($($arg:tt)+) => (log!(1u32, $($arg)+)) )
719-
macro_rules! warn ( ($($arg:tt)+) => (log!(2u32, $($arg)+)) )
720-
macro_rules! info ( ($($arg:tt)+) => (log!(3u32, $($arg)+)) )
721-
macro_rules! debug( ($($arg:tt)+) => (
722-
if cfg!(debug) { log!(4u32, $($arg)+) }
714+
macro_rules! error( ($($arg:tt)*) => (log!(1u32, $($arg)*)) )
715+
macro_rules! warn ( ($($arg:tt)*) => (log!(2u32, $($arg)*)) )
716+
macro_rules! info ( ($($arg:tt)*) => (log!(3u32, $($arg)*)) )
717+
macro_rules! debug( ($($arg:tt)*) => (
718+
if cfg!(debug) { log!(4u32, $($arg)*) }
723719
))
724720

725721
macro_rules! log2(
@@ -730,11 +726,11 @@ pub fn std_macros() -> @str {
730726
}
731727
})
732728
)
733-
macro_rules! error2( ($($arg:tt)+) => (log2!(1u32, $($arg)+)) )
734-
macro_rules! warn2 ( ($($arg:tt)+) => (log2!(2u32, $($arg)+)) )
735-
macro_rules! info2 ( ($($arg:tt)+) => (log2!(3u32, $($arg)+)) )
736-
macro_rules! debug2( ($($arg:tt)+) => (
737-
if cfg!(debug) { log2!(4u32, $($arg)+) }
729+
macro_rules! error2( ($($arg:tt)*) => (log2!(1u32, $($arg)*)) )
730+
macro_rules! warn2 ( ($($arg:tt)*) => (log2!(2u32, $($arg)*)) )
731+
macro_rules! info2 ( ($($arg:tt)*) => (log2!(3u32, $($arg)*)) )
732+
macro_rules! debug2( ($($arg:tt)*) => (
733+
if cfg!(debug) { log2!(4u32, $($arg)*) }
738734
))
739735

740736
macro_rules! fail(
@@ -753,8 +749,8 @@ pub fn std_macros() -> @str {
753749
() => (
754750
fail!(\"explicit failure\")
755751
);
756-
($($arg:tt)+) => (
757-
::std::sys::FailWithCause::fail_with(format!($($arg)+), file!(), line!())
752+
($($arg:tt)*) => (
753+
::std::sys::FailWithCause::fail_with(format!($($arg)*), file!(), line!())
758754
)
759755
)
760756

@@ -958,17 +954,25 @@ pub fn std_macros() -> @str {
958954
)
959955
)
960956

957+
macro_rules! format(($($arg:tt)*) => (
958+
format_args!(::std::fmt::format, $($arg)*)
959+
))
960+
macro_rules! write(($dst:expr, $($arg:tt)*) => (
961+
format_args!(|args| { ::std::fmt::write($dst, args) }, $($arg)*)
962+
))
963+
macro_rules! writeln(($dst:expr, $($arg:tt)*) => (
964+
format_args!(|args| { ::std::fmt::writeln($dst, args) }, $($arg)*)
965+
))
961966
// FIXME(#6846) once stdio is redesigned, this shouldn't perform an
962967
// allocation but should rather delegate to an invocation of
963968
// write! instead of format!
964969
macro_rules! print (
965-
($($arg:tt)+) => (::std::io::print(format!($($arg)+)))
970+
($($arg:tt)*) => (::std::io::print(format!($($arg)*)))
966971
)
967-
968972
// FIXME(#6846) once stdio is redesigned, this shouldn't perform an
969973
// allocation but should rather delegate to an io::Writer
970974
macro_rules! println (
971-
($($arg:tt)+) => (::std::io::println(format!($($arg)+)))
975+
($($arg:tt)*) => (::std::io::println(format!($($arg)*)))
972976
)
973977

974978
// NOTE: use this after a snapshot lands to abstract the details
@@ -1262,6 +1266,20 @@ pub fn mtwt_cancel_outer_mark(tts: &[ast::token_tree], ctxt: ast::SyntaxContext)
12621266
mark_tts(tts,outer_mark)
12631267
}
12641268

1269+
fn original_span(cx: @ExtCtxt) -> @codemap::ExpnInfo {
1270+
let mut relevant_info = cx.backtrace();
1271+
let mut einfo = relevant_info.unwrap();
1272+
loop {
1273+
match relevant_info {
1274+
None => { break }
1275+
Some(e) => {
1276+
einfo = e;
1277+
relevant_info = einfo.call_site.expn_info;
1278+
}
1279+
}
1280+
}
1281+
return einfo;
1282+
}
12651283

12661284
#[cfg(test)]
12671285
mod test {

0 commit comments

Comments
 (0)