Skip to content

Create a Default trait for an empty format string #8564

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 2 commits into from
Closed
Show file tree
Hide file tree
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
56 changes: 53 additions & 3 deletions src/libstd/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,28 +356,46 @@ pub struct Argument<'self> {
priv value: &'self util::Void,
}

/// When a format is not otherwise specified, types are formatted by ascribing
/// to this trait. There is not an explicit way of selecting this trait to be
/// used for formatting, it is only if no other format is specified.
#[allow(missing_doc)]
pub trait Default { fn fmt(&Self, &mut Formatter); }

/// Format trait for the `b` character
#[allow(missing_doc)]
pub trait Bool { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `c` character
#[allow(missing_doc)]
pub trait Char { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `i` and `d` characters
#[allow(missing_doc)]
pub trait Signed { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `u` character
#[allow(missing_doc)]
pub trait Unsigned { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `o` character
#[allow(missing_doc)]
pub trait Octal { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `b` character
#[allow(missing_doc)]
pub trait Binary { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `x` character
#[allow(missing_doc)]
pub trait LowerHex { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `X` character
#[allow(missing_doc)]
pub trait UpperHex { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `s` character
#[allow(missing_doc)]
pub trait String { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `?` character
#[allow(missing_doc)]
pub trait Poly { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `p` character
#[allow(missing_doc)]
pub trait Pointer { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `f` character
#[allow(missing_doc)]
pub trait Float { fn fmt(&Self, &mut Formatter); }

Expand Down Expand Up @@ -726,9 +744,9 @@ impl Bool for bool {
}
}

impl<'self> String for &'self str {
fn fmt(s: & &'self str, f: &mut Formatter) {
f.pad(*s);
impl<'self, T: str::Str> String for T {
fn fmt(s: &T, f: &mut Formatter) {
f.pad(s.as_slice());
}
}

Expand Down Expand Up @@ -855,5 +873,37 @@ impl<T> Pointer for *const T {
}
}

// Implementation of Default for various core types

macro_rules! delegate(($ty:ty to $other:ident) => {
impl<'self> Default for $ty {
fn fmt(me: &$ty, f: &mut Formatter) {
$other::fmt(me, f)
}
}
})
delegate!(int to Signed)
delegate!( i8 to Signed)
delegate!(i16 to Signed)
delegate!(i32 to Signed)
delegate!(i64 to Signed)
delegate!(uint to Unsigned)
delegate!( u8 to Unsigned)
delegate!( u16 to Unsigned)
delegate!( u32 to Unsigned)
delegate!( u64 to Unsigned)
delegate!(@str to String)
delegate!(~str to String)
delegate!(&'self str to String)
delegate!(bool to Bool)
delegate!(char to Char)
delegate!(float to Float)
delegate!(f32 to Float)
delegate!(f64 to Float)

impl<T> Default for *const T {
fn fmt(me: &*const T, f: &mut Formatter) { Pointer::fmt(me, 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.
6 changes: 5 additions & 1 deletion src/libstd/fmt/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,11 @@ impl<'self> Parser<'self> {
}
}
// Finally the actual format specifier
spec.ty = self.word();
if self.consume('?') {
spec.ty = "?";
} else {
spec.ty = self.word();
}
return spec;
}

Expand Down
43 changes: 20 additions & 23 deletions src/libsyntax/ext/ifmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,19 +623,16 @@ impl Context {

fn format_arg(&self, sp: span, arg: Either<uint, @str>,
ident: ast::ident) -> @ast::expr {
let mut ty = match arg {
let ty = match arg {
Left(i) => self.arg_types[i].unwrap(),
Right(s) => *self.name_types.get(&s)
};
// Default types to '?' if nothing else is specified.
if ty == Unknown {
ty = Known(@"?");
}

let argptr = self.ecx.expr_addr_of(sp, self.ecx.expr_ident(sp, ident));
match ty {
let fmt_trait = match ty {
Unknown => "Default",
Known(tyname) => {
let fmt_trait = match tyname.as_slice() {
match tyname.as_slice() {
"?" => "Poly",
"b" => "Bool",
"c" => "Char",
Expand All @@ -653,35 +650,35 @@ impl Context {
`%s`", tyname));
"Dummy"
}
};
let format_fn = self.ecx.path_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of(fmt_trait),
self.ecx.ident_of("fmt"),
]);
self.ecx.expr_call_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("argument"),
], ~[self.ecx.expr_path(format_fn), argptr])
}
}
String => {
self.ecx.expr_call_global(sp, ~[
return self.ecx.expr_call_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("argumentstr"),
], ~[argptr])
}
Unsigned => {
self.ecx.expr_call_global(sp, ~[
return self.ecx.expr_call_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("argumentuint"),
], ~[argptr])
}
Unknown => { fail!() }
}
};

let format_fn = self.ecx.path_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of(fmt_trait),
self.ecx.ident_of("fmt"),
]);
self.ecx.expr_call_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("argument"),
], ~[self.ecx.expr_path(format_fn), argptr])
}
}

Expand Down
32 changes: 27 additions & 5 deletions src/test/run-pass/ifmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,36 @@ pub fn main() {
macro_rules! t(($a:expr, $b:expr) => { 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\")");
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 {");

// default formatters should work
t!(ifmt!("{}", 1i), "1");
t!(ifmt!("{}", 1i8), "1");
t!(ifmt!("{}", 1i16), "1");
t!(ifmt!("{}", 1i32), "1");
t!(ifmt!("{}", 1i64), "1");
t!(ifmt!("{}", 1u), "1");
t!(ifmt!("{}", 1u8), "1");
t!(ifmt!("{}", 1u16), "1");
t!(ifmt!("{}", 1u32), "1");
t!(ifmt!("{}", 1u64), "1");
t!(ifmt!("{}", 1.0f), "1");
t!(ifmt!("{}", 1.0f32), "1");
t!(ifmt!("{}", 1.0f64), "1");
t!(ifmt!("{}", "a"), "a");
t!(ifmt!("{}", ~"a"), "a");
t!(ifmt!("{}", @"a"), "a");
t!(ifmt!("{}", false), "false");
t!(ifmt!("{}", 'a'), "a");

// At least exercise all the formats
t!(ifmt!("{:b}", true), "true");
t!(ifmt!("{:c}", '☃'), "☃");
Expand All @@ -45,6 +65,8 @@ pub fn main() {
t!(ifmt!("{:x}", 10u), "a");
t!(ifmt!("{:X}", 10u), "A");
t!(ifmt!("{:s}", "foo"), "foo");
t!(ifmt!("{:s}", ~"foo"), "foo");
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");
Expand All @@ -54,7 +76,7 @@ pub fn main() {
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\"");
t!(ifmt!("{} {0}", "a"), "a a");

// Methods should probably work
t!(ifmt!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 0u), "c0");
Expand Down