Skip to content

Add initial support for a new formatting syntax #8245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/libstd/either.rs
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ use vec;
use vec::{OwnedVector, ImmutableVector};

/// `Either` is a type that represents one of two alternatives
#[deriving(Clone, Eq)]
#[deriving(Clone, Eq, IterBytes)]
pub enum Either<L, R> {
Left(L),
Right(R)
368 changes: 368 additions & 0 deletions src/libstd/fmt/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,368 @@
// 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.

use prelude::*;

use cast;
use int;
use rt::io::Decorator;
use rt::io::mem::MemWriter;
use rt::io;
use str;
use sys;
use uint;
use util;
use vec;

pub mod parse;
pub mod rt;

/// A struct to represent both where to emit formatting strings to and how they
/// should be formatted. A mutable version of this is passed to all formatting
/// traits.
pub struct Formatter<'self> {
/// Flags for formatting (packed version of rt::Flag)
flags: uint,
/// Character used as 'fill' whenever there is alignment
fill: char,
/// Boolean indication of whether the output should be left-aligned
alignleft: bool,
/// Optionally specified integer width that the output should be
width: Option<uint>,
/// Optionally specified precision for numeric types
precision: Option<uint>,

/// Output buffer.
buf: &'self mut io::Writer,

priv curarg: vec::VecIterator<'self, Argument<'self>>,
priv args: &'self [Argument<'self>],
}

/// This struct represents the generic "argument" which is taken by the Xprintf
/// family of functions. It contains a function to format the given value. At
/// compile time it is ensured that the function and the value have the correct
/// types, and then this struct is used to canonicalize arguments to one type.
pub struct Argument<'self> {
priv formatter: extern "Rust" fn(&util::Void, &mut Formatter),
priv value: &'self util::Void,
}

#[allow(missing_doc)]
pub trait Bool { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Char { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Signed { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Unsigned { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Octal { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Binary { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait LowerHex { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait UpperHex { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait String { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Poly { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Pointer { fn fmt(&Self, &mut Formatter); }

/// The sprintf function takes a precompiled format string and a list of
/// arguments, to return the resulting formatted string.
///
/// This is currently an unsafe function because the types of all arguments
/// aren't verified by immediate callers of this function. This currently does
/// not validate that the correct types of arguments are specified for each
/// format specifier, nor that each argument itself contains the right function
/// for formatting the right type value. Because of this, the function is marked
/// as `unsafe` if this is being called manually.
///
/// Thankfully the rust compiler provides the macro `ifmt!` which will perform
/// all of this validation at compile-time and provides a safe interface for
/// invoking this function.
///
/// # Arguments
///
/// * fmts - the precompiled format string to emit.
/// * args - the list of arguments to the format string. These are only the
/// positional arguments (not named)
///
/// Note that this function assumes that there are enough arguments for the
/// format string.
pub unsafe fn sprintf(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
let output = MemWriter::new();
{
let mut formatter = Formatter {
flags: 0,
width: None,
precision: None,
// FIXME(#8248): shouldn't need a transmute
buf: cast::transmute(&output as &io::Writer),
alignleft: false,
fill: ' ',
args: args,
curarg: args.iter(),
};
for piece in fmt.iter() {
formatter.run(piece, None);
}
}
return str::from_bytes_owned(output.inner());
}

impl<'self> Formatter<'self> {
fn run(&mut self, piece: &rt::Piece, cur: Option<&str>) {
let setcount = |slot: &mut Option<uint>, cnt: &parse::Count| {
match *cnt {
parse::CountIs(n) => { *slot = Some(n); }
parse::CountImplied => { *slot = None; }
parse::CountIsParam(i) => {
let v = self.args[i].value;
unsafe { *slot = Some(*(v as *util::Void as *uint)); }
}
parse::CountIsNextParam => {
let v = self.curarg.next().unwrap().value;
unsafe { *slot = Some(*(v as *util::Void as *uint)); }
}
}
};

match *piece {
rt::String(s) => { self.buf.write(s.as_bytes()); }
rt::CurrentArgument(()) => { self.buf.write(cur.unwrap().as_bytes()); }
rt::Argument(ref arg) => {
// Fill in the format parameters into the formatter
self.fill = arg.format.fill;
self.alignleft = arg.format.alignleft;
self.flags = arg.format.flags;
setcount(&mut self.width, &arg.format.width);
setcount(&mut self.precision, &arg.format.precision);

// Extract the correct argument
let value = match arg.position {
rt::ArgumentNext => { *self.curarg.next().unwrap() }
rt::ArgumentIs(i) => self.args[i],
};

// Then actually do some printing
match arg.method {
None => { (value.formatter)(value.value, self); }
Some(ref method) => { self.execute(*method, value); }
}
}
}
}

fn execute(&mut self, method: &rt::Method, arg: Argument) {
match *method {
// Pluralization is selection upon a numeric value specified as the
// parameter.
rt::Plural(offset, ref selectors, ref default) => {
// This is validated at compile-time to be a pointer to a
// '&uint' value.
let value: &uint = unsafe { cast::transmute(arg.value) };
let value = *value;

// First, attempt to match against explicit values without the
// offsetted value
for s in selectors.iter() {
match s.selector {
Right(val) if value == val => {
return self.runplural(value, s.result);
}
_ => {}
}
}

// Next, offset the value and attempt to match against the
// keyword selectors.
let value = value - match offset { Some(i) => i, None => 0 };
for s in selectors.iter() {
let run = match s.selector {
Left(parse::Zero) => value == 0,
Left(parse::One) => value == 1,
Left(parse::Two) => value == 2,

// XXX: Few/Many should have a user-specified boundary
// One possible option would be in the function
// pointer of the 'arg: Argument' struct.
Left(parse::Few) => value < 8,
Left(parse::Many) => value >= 8,

Right(*) => false
};
if run {
return self.runplural(value, s.result);
}
}

self.runplural(value, *default);
}

// Select is just a matching against the string specified.
rt::Select(ref selectors, ref default) => {
// This is validated at compile-time to be a pointer to a
// string slice,
let value: & &str = unsafe { cast::transmute(arg.value) };
let value = *value;

for s in selectors.iter() {
if s.selector == value {
for piece in s.result.iter() {
self.run(piece, Some(value));
}
return;
}
}
for piece in default.iter() {
self.run(piece, Some(value));
}
}
}
}

fn runplural(&mut self, value: uint, pieces: &[rt::Piece]) {
do uint::to_str_bytes(value, 10) |buf| {
let valuestr = str::from_bytes_slice(buf);
for piece in pieces.iter() {
self.run(piece, Some(valuestr));
}
}
}
}

/// This is a function which calls are emitted to by the compiler itself to
/// create the Argument structures that are passed into the `sprintf` function.
#[doc(hidden)]
pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter),
t: &'a T) -> Argument<'a> {
unsafe {
Argument {
formatter: cast::transmute(f),
value: cast::transmute(t)
}
}
}

/// When the compiler determines that the type of an argument *must* be a string
/// (such as for select), then it invokes this method.
#[doc(hidden)]
pub fn argumentstr<'a>(s: &'a &str) -> Argument<'a> {
argument(String::fmt, s)
}

/// When the compiler determines that the type of an argument *must* be a uint
/// (such as for plural), then it invokes this method.
#[doc(hidden)]
pub fn argumentuint<'a>(s: &'a uint) -> Argument<'a> {
argument(Unsigned::fmt, s)
}

// Implementations of the core formatting traits

impl Bool for bool {
fn fmt(b: &bool, f: &mut Formatter) {
String::fmt(&(if *b {"true"} else {"false"}), f);
}
}

impl<'self> String for &'self str {
fn fmt(s: & &'self str, f: &mut Formatter) {
// XXX: formatting args
f.buf.write(s.as_bytes())
}
}

impl Char for char {
fn fmt(c: &char, f: &mut Formatter) {
// XXX: formatting args
// XXX: shouldn't require an allocation
let mut s = ~"";
s.push_char(*c);
f.buf.write(s.as_bytes());
}
}

impl Signed for int {
fn fmt(c: &int, f: &mut Formatter) {
// XXX: formatting args
do int::to_str_bytes(*c, 10) |buf| {
f.buf.write(buf);
}
}
}

impl Unsigned for uint {
fn fmt(c: &uint, f: &mut Formatter) {
// XXX: formatting args
do uint::to_str_bytes(*c, 10) |buf| {
f.buf.write(buf);
}
}
}

impl Octal for uint {
fn fmt(c: &uint, f: &mut Formatter) {
// XXX: formatting args
do uint::to_str_bytes(*c, 8) |buf| {
f.buf.write(buf);
}
}
}

impl LowerHex for uint {
fn fmt(c: &uint, f: &mut Formatter) {
// XXX: formatting args
do uint::to_str_bytes(*c, 16) |buf| {
f.buf.write(buf);
}
}
}

impl UpperHex for uint {
fn fmt(c: &uint, f: &mut Formatter) {
// XXX: formatting args
do uint::to_str_bytes(*c, 16) |buf| {
let mut local = [0u8, ..16];
for (l, &b) in local.mut_iter().zip(buf.iter()) {
*l = match b as char {
'a' .. 'f' => (b - 'a' as u8) + 'A' as u8,
_ => b,
};
}
f.buf.write(local.slice_to(buf.len()));
}
}
}

impl<T> Poly for T {
fn fmt(t: &T, f: &mut Formatter) {
// XXX: formatting args
let s = sys::log_str(t);
f.buf.write(s.as_bytes());
}
}

// n.b. use 'const' to get an implementation for both '*mut' and '*' at the same
// time.
impl<T> Pointer for *const T {
fn fmt(t: &*const T, f: &mut Formatter) {
// XXX: formatting args
f.buf.write("0x".as_bytes());
LowerHex::fmt(&(*t as uint), f);
}
}

// If you expected tests to be here, look instead at the run-pass/ifmt.rs test,
// it's a lot easier than creating all of the rt::Piece structures here.
896 changes: 896 additions & 0 deletions src/libstd/fmt/parse.rs

Large diffs are not rendered by default.

62 changes: 62 additions & 0 deletions src/libstd/fmt/rt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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.

//! This is an internal module used by the ifmt! runtime. These structures are
//! emitted to static arrays to precompile format strings ahead of time.
//!
//! These definitions are similar to their `ct` equivalents, but differ in that
//! these can be statically allocated and are slightly optimized for the runtime
#[allow(missing_doc)];
#[doc(hidden)];

use either::Either;
use fmt::parse;
use option::Option;

pub enum Piece<'self> {
String(&'self str),
// FIXME(#8259): this shouldn't require the unit-value here
CurrentArgument(()),
Argument(Argument<'self>),
}

pub struct Argument<'self> {
position: Position,
format: FormatSpec,
method: Option<&'self Method<'self>>
}

pub struct FormatSpec {
fill: char,
alignleft: bool,
flags: uint,
precision: parse::Count,
width: parse::Count,
}

pub enum Position {
ArgumentNext, ArgumentIs(uint)
}

pub enum Method<'self> {
Plural(Option<uint>, &'self [PluralArm<'self>], &'self [Piece<'self>]),
Select(&'self [SelectArm<'self>], &'self [Piece<'self>]),
}

pub struct PluralArm<'self> {
selector: Either<parse::PluralKeyword, uint>,
result: &'self [Piece<'self>],
}

pub struct SelectArm<'self> {
selector: &'self str,
result: &'self [Piece<'self>],
}
2 changes: 1 addition & 1 deletion src/libstd/rt/io/mem.rs
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ pub struct MemWriter {
}

impl MemWriter {
pub fn new() -> MemWriter { MemWriter { buf: ~[] } }
pub fn new() -> MemWriter { MemWriter { buf: vec::with_capacity(128) } }
}

impl Writer for MemWriter {
3 changes: 3 additions & 0 deletions src/libstd/std.rs
Original file line number Diff line number Diff line change
@@ -177,6 +177,7 @@ pub mod rand;
pub mod run;
pub mod sys;
pub mod cast;
pub mod fmt;
pub mod repr;
pub mod cleanup;
pub mod reflect;
@@ -216,4 +217,6 @@ mod std {
pub use unstable;
pub use str;
pub use os;
pub use fmt;
pub use to_bytes;
}
2 changes: 2 additions & 0 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
@@ -139,6 +139,8 @@ pub fn syntax_expander_table() -> SyntaxEnv {
ext::tt::macro_rules::add_new_extension));
syntax_expanders.insert(intern(&"fmt"),
builtin_normal_tt(ext::fmt::expand_syntax_ext));
syntax_expanders.insert(intern(&"ifmt"),
builtin_normal_tt(ext::ifmt::expand_syntax_ext));
syntax_expanders.insert(
intern(&"auto_encode"),
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
4 changes: 3 additions & 1 deletion src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
@@ -1014,7 +1014,9 @@ pub fn expand_crate(parse_sess: @mut parse::ParseSess,
.. *afp};
let f = make_fold(f_pre);

@f.fold_crate(c)
let ret = @f.fold_crate(c);
parse_sess.span_diagnostic.handler().abort_if_errors();
return ret;
}

// given a function from idents to idents, produce
720 changes: 720 additions & 0 deletions src/libsyntax/ext/ifmt.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/libsyntax/syntax.rs
Original file line number Diff line number Diff line change
@@ -73,6 +73,7 @@ pub mod ext {

pub mod cfg;
pub mod fmt;
pub mod ifmt;
pub mod env;
pub mod bytes;
pub mod concat_idents;
74 changes: 74 additions & 0 deletions src/test/compile-fail/ifmt-bad-arg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// 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() {
// bad arguments to the ifmt! call

ifmt!(); //~ ERROR: expects at least one
ifmt!("{}"); //~ ERROR: invalid reference to argument

ifmt!("{1}", 1); //~ ERROR: invalid reference to argument `1`
//~^ ERROR: argument never used
ifmt!("{foo}"); //~ ERROR: no argument named `foo`

ifmt!("{}", 1, 2); //~ ERROR: argument never used
ifmt!("{1}", 1, 2); //~ ERROR: argument never used
ifmt!("{}", 1, foo=2); //~ ERROR: named argument never used
ifmt!("{foo}", 1, foo=2); //~ ERROR: argument never used
ifmt!("", foo=2); //~ ERROR: named argument never used

ifmt!("{0:d} {0:s}", 1); //~ ERROR: redeclared with type `s`
ifmt!("{foo:d} {foo:s}", foo=1); //~ ERROR: redeclared with type `s`

ifmt!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument
ifmt!("#"); //~ ERROR: `#` reference used
ifmt!("", foo=1, 2); //~ ERROR: positional arguments cannot follow
ifmt!("" 1); //~ ERROR: expected token: `,`
ifmt!("", 1 1); //~ ERROR: expected token: `,`

ifmt!("{0, select, a{} a{} other{}}", "a"); //~ ERROR: duplicate selector
ifmt!("{0, plural, =1{} =1{} other{}}", 1u); //~ ERROR: duplicate selector
ifmt!("{0, plural, one{} one{} other{}}", 1u); //~ ERROR: duplicate selector

// bad syntax of the format string

ifmt!("{"); //~ ERROR: unterminated format string
ifmt!("\\ "); //~ ERROR: invalid escape
ifmt!("\\"); //~ ERROR: expected an escape

ifmt!("{0, }", 1); //~ ERROR: expected method
ifmt!("{0, foo}", 1); //~ ERROR: unknown method
ifmt!("{0, select}", "a"); //~ ERROR: must be followed by
ifmt!("{0, plural}", 1); //~ ERROR: must be followed by

ifmt!("{0, select, a{{}", 1); //~ ERROR: must be terminated
ifmt!("{0, select, {} other{}}", "a"); //~ ERROR: empty selector
ifmt!("{0, select, other{} other{}}", "a"); //~ ERROR: multiple `other`
ifmt!("{0, plural, offset: other{}}", "a"); //~ ERROR: must be an integer
ifmt!("{0, plural, offset 1 other{}}", "a"); //~ ERROR: be followed by `:`
ifmt!("{0, plural, =a{} other{}}", "a"); //~ ERROR: followed by an integer
ifmt!("{0, plural, a{} other{}}", "a"); //~ ERROR: unexpected plural
ifmt!("{0, select, a{}}", "a"); //~ ERROR: must provide an `other`
ifmt!("{0, plural, =1{}}", "a"); //~ ERROR: must provide an `other`

ifmt!("{0, plural, other{{0:s}}}", "a"); //~ ERROR: previously used as
ifmt!("{:s} {0, plural, other{}}", "a"); //~ ERROR: argument used to
ifmt!("{0, select, other{}} \
{0, plural, other{}}", "a");
//~^ ERROR: declared with multiple formats

// It should be illegal to use implicit placement arguments nested inside of
// format strings because otherwise the "internal pointer of which argument
// is next" would be invalidated if different cases had different numbers of
// arguments.
ifmt!("{0, select, other{{}}}", "a"); //~ ERROR: cannot use implicit
ifmt!("{0, plural, other{{}}}", 1); //~ ERROR: cannot use implicit
ifmt!("{0, plural, other{{1:.*d}}}", 1, 2); //~ ERROR: cannot use implicit
}
14 changes: 14 additions & 0 deletions src/test/compile-fail/ifmt-bad-plural.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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() {
ifmt!("{0, plural, other{}}", "a");
//~^ ERROR: expected uint but found
}
14 changes: 14 additions & 0 deletions src/test/compile-fail/ifmt-bad-select.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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() {
ifmt!("{0, select, other{}}", 2);
//~^ ERROR: expected &str but found integral
}
14 changes: 14 additions & 0 deletions src/test/compile-fail/ifmt-unimpl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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() {
ifmt!("{:d}", "3");
//~^ ERROR: failed to find an implementation of trait std::fmt::Signed
}
14 changes: 14 additions & 0 deletions src/test/compile-fail/ifmt-unknown-trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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() {
ifmt!("{:notimplemented}", "3");
//~^ ERROR: unknown format trait `notimplemented`
}
71 changes: 71 additions & 0 deletions src/test/run-pass/ifmt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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.

use std::fmt;

struct A;
struct B;

#[fmt="foo"]
impl fmt::Signed for A {
fn fmt(_: &A, f: &mut fmt::Formatter) { f.buf.write("aloha".as_bytes()); }
}
impl fmt::Signed for B {
fn fmt(_: &B, f: &mut fmt::Formatter) { f.buf.write("adios".as_bytes()); }
}

pub fn main() {
fn t(a: ~str, b: &str) { assert_eq!(a, b.to_owned()); }

// Make sure there's a poly formatter that takes anything
t(ifmt!("{}", 1), "1");
t(ifmt!("{}", A), "{}");
t(ifmt!("{}", ()), "()");
t(ifmt!("{}", @(~1, "foo")), "@(~1, \"foo\")");

// Various edge cases without formats
t(ifmt!(""), "");
t(ifmt!("hello"), "hello");
t(ifmt!("hello \\{"), "hello {");

// At least exercise all the formats
t(ifmt!("{:b}", true), "true");
t(ifmt!("{:c}", '☃'), "☃");
t(ifmt!("{:d}", 10), "10");
t(ifmt!("{:i}", 10), "10");
t(ifmt!("{:u}", 10u), "10");
t(ifmt!("{:o}", 10u), "12");
t(ifmt!("{:x}", 10u), "a");
t(ifmt!("{:X}", 10u), "A");
t(ifmt!("{:s}", "foo"), "foo");
t(ifmt!("{:p}", 0x1234 as *int), "0x1234");
t(ifmt!("{:p}", 0x1234 as *mut int), "0x1234");
t(ifmt!("{:d}", A), "aloha");
t(ifmt!("{:d}", B), "adios");
t(ifmt!("foo {:s} ☃☃☃☃☃☃", "bar"), "foo bar ☃☃☃☃☃☃");
t(ifmt!("{1} {0}", 0, 1), "1 0");
t(ifmt!("{foo} {bar}", foo=0, bar=1), "0 1");
t(ifmt!("{foo} {1} {bar} {0}", 0, 1, foo=2, bar=3), "2 1 3 0");
t(ifmt!("{} {0:s}", "a"), "a a");
t(ifmt!("{} {0}", "a"), "\"a\" \"a\"");

// Methods should probably work
t(ifmt!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 0u), "c0");
t(ifmt!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 1u), "a1");
t(ifmt!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 2u), "b2");
t(ifmt!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 3u), "d3");
t(ifmt!("{0, select, a{a#} b{b#} c{c#} other{d#}}", "a"), "aa");
t(ifmt!("{0, select, a{a#} b{b#} c{c#} other{d#}}", "b"), "bb");
t(ifmt!("{0, select, a{a#} b{b#} c{c#} other{d#}}", "c"), "cc");
t(ifmt!("{0, select, a{a#} b{b#} c{c#} other{d#}}", "d"), "dd");
t(ifmt!("{1, select, a{#{0:s}} other{#{1}}}", "b", "a"), "ab");
t(ifmt!("{1, select, a{#{0}} other{#{1}}}", "c", "b"), "bb");
}