2012-12-03 18:48:01 -06:00
|
|
|
// Copyright 2012 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.
|
|
|
|
|
2011-08-27 23:27:39 -05:00
|
|
|
/*
|
2012-10-12 14:32:36 -05:00
|
|
|
* The compiler code necessary to support the fmt! extension. Eventually this
|
2011-08-27 23:27:39 -05:00
|
|
|
* should all get sucked into either the standard library extfmt module or the
|
|
|
|
* compiler syntax extension plugin interface.
|
|
|
|
*/
|
2012-12-23 16:41:37 -06:00
|
|
|
|
|
|
|
use ast;
|
2012-09-04 13:37:29 -05:00
|
|
|
use codemap::span;
|
2012-12-23 16:41:37 -06:00
|
|
|
use ext::base::*;
|
|
|
|
use ext::base;
|
2013-02-25 13:11:21 -06:00
|
|
|
use ext::build;
|
2012-09-04 13:37:29 -05:00
|
|
|
use ext::build::*;
|
2013-03-01 12:44:43 -06:00
|
|
|
|
|
|
|
use core::unstable::extfmt::ct::*;
|
2012-12-23 16:41:37 -06:00
|
|
|
|
2013-03-12 15:00:50 -05:00
|
|
|
pub fn expand_syntax_ext(cx: @ext_ctxt, sp: span, tts: &[ast::token_tree])
|
2013-01-22 18:45:27 -06:00
|
|
|
-> base::MacResult {
|
2013-02-18 23:25:44 -06:00
|
|
|
let args = get_exprs_from_tts(cx, tts);
|
2012-12-12 19:08:09 -06:00
|
|
|
if args.len() == 0 {
|
|
|
|
cx.span_fatal(sp, "fmt! takes at least 1 argument.");
|
|
|
|
}
|
2011-08-27 23:27:39 -05:00
|
|
|
let fmt =
|
|
|
|
expr_to_str(cx, args[0],
|
2012-10-12 14:32:36 -05:00
|
|
|
~"first argument to fmt! must be a string literal.");
|
2011-08-27 23:27:39 -05:00
|
|
|
let fmtspan = args[0].span;
|
2013-03-08 14:39:42 -06:00
|
|
|
debug!("Format string: %s", fmt);
|
2013-03-12 15:00:50 -05:00
|
|
|
fn parse_fmt_err_(cx: @ext_ctxt, sp: span, msg: &str) -> ! {
|
2011-08-27 23:27:39 -05:00
|
|
|
cx.span_fatal(sp, msg);
|
|
|
|
}
|
2013-03-01 15:30:06 -06:00
|
|
|
let parse_fmt_err: @fn(&str) -> ! = |s| parse_fmt_err_(cx, fmtspan, s);
|
2011-08-27 23:27:39 -05:00
|
|
|
let pieces = parse_fmt_string(fmt, parse_fmt_err);
|
2013-01-22 18:45:27 -06:00
|
|
|
MRExpr(pieces_to_expr(cx, sp, pieces, args))
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
|
2012-06-21 18:44:10 -05:00
|
|
|
// FIXME (#2249): A lot of these functions for producing expressions can
|
|
|
|
// probably be factored out in common with other code that builds
|
|
|
|
// expressions. Also: Cleanup the naming of these functions.
|
2013-01-29 18:18:59 -06:00
|
|
|
// Note: Moved many of the common ones to build.rs --kevina
|
2013-03-12 15:00:50 -05:00
|
|
|
fn pieces_to_expr(cx: @ext_ctxt, sp: span,
|
2012-09-11 21:37:29 -05:00
|
|
|
pieces: ~[Piece], args: ~[@ast::expr])
|
2011-09-12 04:27:30 -05:00
|
|
|
-> @ast::expr {
|
2013-03-12 15:00:50 -05:00
|
|
|
fn make_path_vec(cx: @ext_ctxt, ident: @~str) -> ~[ast::ident] {
|
2013-01-24 12:33:20 -06:00
|
|
|
let intr = cx.parse_sess().interner;
|
2013-02-27 21:53:31 -06:00
|
|
|
return ~[intr.intern(@~"unstable"), intr.intern(@~"extfmt"),
|
2013-02-20 14:08:25 -06:00
|
|
|
intr.intern(@~"rt"), intr.intern(ident)];
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2013-03-12 15:00:50 -05:00
|
|
|
fn make_rt_path_expr(cx: @ext_ctxt, sp: span, nm: @~str) -> @ast::expr {
|
2012-07-18 18:18:02 -05:00
|
|
|
let path = make_path_vec(cx, nm);
|
2012-12-23 16:41:37 -06:00
|
|
|
return mk_path_global(cx, sp, path);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
// Produces an AST expression that represents a RT::conv record,
|
|
|
|
// which tells the RT::conv* functions how to perform the conversion
|
|
|
|
|
2013-03-19 20:24:01 -05:00
|
|
|
fn make_rt_conv_expr(cx: @ext_ctxt, sp: span, cnv: &Conv) -> @ast::expr {
|
2013-03-12 15:00:50 -05:00
|
|
|
fn make_flags(cx: @ext_ctxt, sp: span, flags: ~[Flag]) -> @ast::expr {
|
2012-07-14 00:57:48 -05:00
|
|
|
let mut tmp_expr = make_rt_path_expr(cx, sp, @~"flag_none");
|
2012-06-30 18:19:07 -05:00
|
|
|
for flags.each |f| {
|
2012-09-19 18:55:01 -05:00
|
|
|
let fstr = match *f {
|
2012-09-11 21:37:29 -05:00
|
|
|
FlagLeftJustify => ~"flag_left_justify",
|
|
|
|
FlagLeftZeroPad => ~"flag_left_zero_pad",
|
|
|
|
FlagSpaceForSign => ~"flag_space_for_sign",
|
|
|
|
FlagSignAlways => ~"flag_sign_always",
|
|
|
|
FlagAlternate => ~"flag_alternate"
|
2012-06-29 01:36:00 -05:00
|
|
|
};
|
|
|
|
tmp_expr = mk_binary(cx, sp, ast::bitor, tmp_expr,
|
|
|
|
make_rt_path_expr(cx, sp, @fstr));
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-08-01 19:30:05 -05:00
|
|
|
return tmp_expr;
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2013-03-12 15:00:50 -05:00
|
|
|
fn make_count(cx: @ext_ctxt, sp: span, cnt: Count) -> @ast::expr {
|
2012-08-06 14:34:08 -05:00
|
|
|
match cnt {
|
2012-09-11 21:37:29 -05:00
|
|
|
CountImplied => {
|
|
|
|
return make_rt_path_expr(cx, sp, @~"CountImplied");
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-09-11 21:37:29 -05:00
|
|
|
CountIs(c) => {
|
2012-11-01 18:47:24 -05:00
|
|
|
let count_lit = mk_uint(cx, sp, c as uint);
|
2012-09-11 21:37:29 -05:00
|
|
|
let count_is_path = make_path_vec(cx, @~"CountIs");
|
2012-06-29 18:26:56 -05:00
|
|
|
let count_is_args = ~[count_lit];
|
2012-12-23 16:41:37 -06:00
|
|
|
return mk_call_global(cx, sp, count_is_path, count_is_args);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-10-12 14:32:36 -05:00
|
|
|
_ => cx.span_unimpl(sp, ~"unimplemented fmt! conversion")
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2013-03-12 15:00:50 -05:00
|
|
|
fn make_ty(cx: @ext_ctxt, sp: span, t: Ty) -> @ast::expr {
|
2013-04-12 00:10:31 -05:00
|
|
|
let rt_type;
|
2012-08-06 14:34:08 -05:00
|
|
|
match t {
|
2012-09-11 21:37:29 -05:00
|
|
|
TyHex(c) => match c {
|
|
|
|
CaseUpper => rt_type = ~"TyHexUpper",
|
|
|
|
CaseLower => rt_type = ~"TyHexLower"
|
2012-08-06 19:14:32 -05:00
|
|
|
},
|
2012-09-11 21:37:29 -05:00
|
|
|
TyBits => rt_type = ~"TyBits",
|
|
|
|
TyOctal => rt_type = ~"TyOctal",
|
|
|
|
_ => rt_type = ~"TyDefault"
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-08-01 19:30:05 -05:00
|
|
|
return make_rt_path_expr(cx, sp, @rt_type);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2013-03-12 15:00:50 -05:00
|
|
|
fn make_conv_struct(cx: @ext_ctxt, sp: span, flags_expr: @ast::expr,
|
2011-08-27 23:27:39 -05:00
|
|
|
width_expr: @ast::expr, precision_expr: @ast::expr,
|
|
|
|
ty_expr: @ast::expr) -> @ast::expr {
|
2012-07-18 18:18:02 -05:00
|
|
|
let intr = cx.parse_sess().interner;
|
2013-01-24 12:33:20 -06:00
|
|
|
mk_global_struct_e(
|
|
|
|
cx,
|
|
|
|
sp,
|
|
|
|
make_path_vec(cx, @~"Conv"),
|
|
|
|
~[
|
2013-02-21 02:16:31 -06:00
|
|
|
build::Field {
|
|
|
|
ident: intr.intern(@~"flags"), ex: flags_expr
|
|
|
|
},
|
|
|
|
build::Field {
|
|
|
|
ident: intr.intern(@~"width"), ex: width_expr
|
|
|
|
},
|
|
|
|
build::Field {
|
|
|
|
ident: intr.intern(@~"precision"), ex: precision_expr
|
|
|
|
},
|
|
|
|
build::Field {
|
|
|
|
ident: intr.intern(@~"ty"), ex: ty_expr
|
|
|
|
},
|
2013-01-24 12:33:20 -06:00
|
|
|
]
|
|
|
|
)
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
let rt_conv_flags = make_flags(cx, sp, cnv.flags);
|
|
|
|
let rt_conv_width = make_count(cx, sp, cnv.width);
|
|
|
|
let rt_conv_precision = make_count(cx, sp, cnv.precision);
|
|
|
|
let rt_conv_ty = make_ty(cx, sp, cnv.ty);
|
2013-01-24 12:33:20 -06:00
|
|
|
make_conv_struct(cx, sp, rt_conv_flags, rt_conv_width,
|
|
|
|
rt_conv_precision, rt_conv_ty)
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2013-03-20 22:33:10 -05:00
|
|
|
fn make_conv_call(cx: @ext_ctxt, sp: span, conv_type: &str, cnv: &Conv,
|
|
|
|
arg: @ast::expr, buf: @ast::expr) -> @ast::expr {
|
2012-07-14 00:57:48 -05:00
|
|
|
let fname = ~"conv_" + conv_type;
|
2012-06-10 02:49:59 -05:00
|
|
|
let path = make_path_vec(cx, @fname);
|
2011-08-27 23:27:39 -05:00
|
|
|
let cnv_expr = make_rt_conv_expr(cx, sp, cnv);
|
2013-03-20 22:33:10 -05:00
|
|
|
let args = ~[cnv_expr, arg, buf];
|
2012-12-23 16:41:37 -06:00
|
|
|
return mk_call_global(cx, arg.span, path, args);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-06-28 18:23:12 -05:00
|
|
|
|
2013-03-20 22:33:10 -05:00
|
|
|
fn make_new_conv(cx: @ext_ctxt, sp: span, cnv: &Conv,
|
|
|
|
arg: @ast::expr, buf: @ast::expr) -> @ast::expr {
|
2013-03-19 20:24:01 -05:00
|
|
|
fn is_signed_type(cnv: &Conv) -> bool {
|
2012-08-06 14:34:08 -05:00
|
|
|
match cnv.ty {
|
2012-09-11 21:37:29 -05:00
|
|
|
TyInt(s) => match s {
|
|
|
|
Signed => return true,
|
|
|
|
Unsigned => return false
|
2012-08-06 19:14:32 -05:00
|
|
|
},
|
2012-09-11 21:37:29 -05:00
|
|
|
TyFloat => return true,
|
2012-08-03 21:59:04 -05:00
|
|
|
_ => return false
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2012-10-12 14:32:36 -05:00
|
|
|
let unsupported = ~"conversion not supported in fmt! string";
|
2012-08-06 14:34:08 -05:00
|
|
|
match cnv.param {
|
2012-08-20 14:23:37 -05:00
|
|
|
option::None => (),
|
2012-08-03 21:59:04 -05:00
|
|
|
_ => cx.span_unimpl(sp, unsupported)
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-06-30 18:19:07 -05:00
|
|
|
for cnv.flags.each |f| {
|
2012-09-19 18:55:01 -05:00
|
|
|
match *f {
|
2012-09-11 21:37:29 -05:00
|
|
|
FlagLeftJustify => (),
|
|
|
|
FlagSignAlways => {
|
2011-08-27 23:27:39 -05:00
|
|
|
if !is_signed_type(cnv) {
|
|
|
|
cx.span_fatal(sp,
|
2012-07-14 00:57:48 -05:00
|
|
|
~"+ flag only valid in " +
|
2012-10-12 14:32:36 -05:00
|
|
|
~"signed fmt! conversion");
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2012-09-11 21:37:29 -05:00
|
|
|
FlagSpaceForSign => {
|
2011-08-27 23:27:39 -05:00
|
|
|
if !is_signed_type(cnv) {
|
|
|
|
cx.span_fatal(sp,
|
2012-07-14 00:57:48 -05:00
|
|
|
~"space flag only valid in " +
|
2012-10-12 14:32:36 -05:00
|
|
|
~"signed fmt! conversions");
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2012-09-11 21:37:29 -05:00
|
|
|
FlagLeftZeroPad => (),
|
2012-08-03 21:59:04 -05:00
|
|
|
_ => cx.span_unimpl(sp, unsupported)
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2012-08-06 14:34:08 -05:00
|
|
|
match cnv.width {
|
2012-09-11 21:37:29 -05:00
|
|
|
CountImplied => (),
|
|
|
|
CountIs(_) => (),
|
2012-08-03 21:59:04 -05:00
|
|
|
_ => cx.span_unimpl(sp, unsupported)
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-08-06 14:34:08 -05:00
|
|
|
match cnv.precision {
|
2012-09-11 21:37:29 -05:00
|
|
|
CountImplied => (),
|
|
|
|
CountIs(_) => (),
|
2012-08-03 21:59:04 -05:00
|
|
|
_ => cx.span_unimpl(sp, unsupported)
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2013-03-20 22:33:10 -05:00
|
|
|
let (name, actual_arg) = match cnv.ty {
|
|
|
|
TyStr => ("str", arg),
|
|
|
|
TyInt(Signed) => ("int", arg),
|
|
|
|
TyBool => ("bool", arg),
|
|
|
|
TyChar => ("char", arg),
|
|
|
|
TyBits | TyOctal | TyHex(_) | TyInt(Unsigned) => ("uint", arg),
|
|
|
|
TyFloat => ("float", arg),
|
|
|
|
TyPoly => ("poly", mk_addr_of(cx, sp, arg))
|
|
|
|
};
|
|
|
|
return make_conv_call(cx, arg.span, name, cnv, actual_arg,
|
|
|
|
mk_mut_addr_of(cx, arg.span, buf));
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2013-03-19 20:24:01 -05:00
|
|
|
fn log_conv(c: &Conv) {
|
2013-03-20 12:01:58 -05:00
|
|
|
debug!("Building conversion:");
|
2012-08-06 14:34:08 -05:00
|
|
|
match c.param {
|
2013-03-08 14:39:42 -06:00
|
|
|
Some(p) => { debug!("param: %s", p.to_str()); }
|
2012-08-22 19:24:52 -05:00
|
|
|
_ => debug!("param: none")
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-06-30 18:19:07 -05:00
|
|
|
for c.flags.each |f| {
|
2012-09-19 18:55:01 -05:00
|
|
|
match *f {
|
2012-09-11 21:37:29 -05:00
|
|
|
FlagLeftJustify => debug!("flag: left justify"),
|
|
|
|
FlagLeftZeroPad => debug!("flag: left zero pad"),
|
|
|
|
FlagSpaceForSign => debug!("flag: left space pad"),
|
|
|
|
FlagSignAlways => debug!("flag: sign always"),
|
|
|
|
FlagAlternate => debug!("flag: alternate")
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2012-08-06 14:34:08 -05:00
|
|
|
match c.width {
|
2013-03-08 14:39:42 -06:00
|
|
|
CountIs(i) =>
|
|
|
|
debug!("width: count is %s", i.to_str()),
|
|
|
|
CountIsParam(i) =>
|
|
|
|
debug!("width: count is param %s", i.to_str()),
|
2012-09-11 21:37:29 -05:00
|
|
|
CountIsNextParam => debug!("width: count is next param"),
|
|
|
|
CountImplied => debug!("width: count is implied")
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-08-06 14:34:08 -05:00
|
|
|
match c.precision {
|
2013-03-08 14:39:42 -06:00
|
|
|
CountIs(i) =>
|
|
|
|
debug!("prec: count is %s", i.to_str()),
|
|
|
|
CountIsParam(i) =>
|
|
|
|
debug!("prec: count is param %s", i.to_str()),
|
2012-09-11 21:37:29 -05:00
|
|
|
CountIsNextParam => debug!("prec: count is next param"),
|
|
|
|
CountImplied => debug!("prec: count is implied")
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-08-06 14:34:08 -05:00
|
|
|
match c.ty {
|
2012-09-11 21:37:29 -05:00
|
|
|
TyBool => debug!("type: bool"),
|
|
|
|
TyStr => debug!("type: str"),
|
|
|
|
TyChar => debug!("type: char"),
|
|
|
|
TyInt(s) => match s {
|
|
|
|
Signed => debug!("type: signed"),
|
|
|
|
Unsigned => debug!("type: unsigned")
|
2012-08-06 19:14:32 -05:00
|
|
|
},
|
2012-09-11 21:37:29 -05:00
|
|
|
TyBits => debug!("type: bits"),
|
|
|
|
TyHex(cs) => match cs {
|
|
|
|
CaseUpper => debug!("type: uhex"),
|
|
|
|
CaseLower => debug!("type: lhex"),
|
2012-08-06 19:14:32 -05:00
|
|
|
},
|
2012-09-11 21:37:29 -05:00
|
|
|
TyOctal => debug!("type: octal"),
|
|
|
|
TyFloat => debug!("type: float"),
|
|
|
|
TyPoly => debug!("type: poly")
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2013-03-20 12:01:58 -05:00
|
|
|
|
2011-08-27 23:27:39 -05:00
|
|
|
let fmt_sp = args[0].span;
|
2012-03-15 08:47:03 -05:00
|
|
|
let mut n = 0u;
|
2012-06-28 18:23:12 -05:00
|
|
|
let nargs = args.len();
|
2013-03-20 22:33:10 -05:00
|
|
|
|
|
|
|
/* 'ident' is the local buffer building up the result of fmt! */
|
|
|
|
let ident = cx.parse_sess().interner.intern(@~"__fmtbuf");
|
|
|
|
let buf = || mk_path(cx, fmt_sp, ~[ident]);
|
|
|
|
let str_ident = cx.parse_sess().interner.intern(@~"str");
|
|
|
|
let push_ident = cx.parse_sess().interner.intern(@~"push_str");
|
|
|
|
let mut stms = ~[];
|
|
|
|
|
|
|
|
/* Translate each piece (portion of the fmt expression) by invoking the
|
|
|
|
corresponding function in core::unstable::extfmt. Each function takes a
|
|
|
|
buffer to insert data into along with the data being formatted. */
|
2013-04-12 00:10:31 -05:00
|
|
|
let npieces = pieces.len();
|
2013-03-20 22:33:10 -05:00
|
|
|
do vec::consume(pieces) |i, pc| {
|
2013-03-20 12:01:58 -05:00
|
|
|
match pc {
|
2013-03-20 22:33:10 -05:00
|
|
|
/* Raw strings get appended via str::push_str */
|
|
|
|
PieceString(s) => {
|
|
|
|
let portion = mk_uniq_str(cx, fmt_sp, s);
|
|
|
|
|
|
|
|
/* If this is the first portion, then initialize the local
|
2013-04-12 00:10:31 -05:00
|
|
|
buffer with it directly. If it's actually the only piece,
|
|
|
|
then there's no need for it to be mutable */
|
2013-03-20 22:33:10 -05:00
|
|
|
if i == 0 {
|
2013-04-12 00:10:31 -05:00
|
|
|
stms.push(mk_local(cx, fmt_sp, npieces > 1, ident, portion));
|
2013-03-20 22:33:10 -05:00
|
|
|
} else {
|
|
|
|
let args = ~[mk_mut_addr_of(cx, fmt_sp, buf()), portion];
|
|
|
|
let call = mk_call_global(cx,
|
|
|
|
fmt_sp,
|
|
|
|
~[str_ident, push_ident],
|
|
|
|
args);
|
|
|
|
stms.push(mk_stmt(cx, fmt_sp, call));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Invoke the correct conv function in extfmt */
|
2013-03-20 12:01:58 -05:00
|
|
|
PieceConv(ref conv) => {
|
|
|
|
n += 1u;
|
|
|
|
if n >= nargs {
|
|
|
|
cx.span_fatal(sp,
|
|
|
|
~"not enough arguments to fmt! " +
|
2012-07-14 00:57:48 -05:00
|
|
|
~"for the given format string");
|
2013-03-20 12:01:58 -05:00
|
|
|
}
|
2013-03-20 22:33:10 -05:00
|
|
|
|
2013-03-20 12:01:58 -05:00
|
|
|
log_conv(conv);
|
2013-03-20 22:33:10 -05:00
|
|
|
/* If the first portion is a conversion, then the local buffer
|
|
|
|
must be initialized as an empty string */
|
|
|
|
if i == 0 {
|
|
|
|
stms.push(mk_local(cx, fmt_sp, true, ident,
|
|
|
|
mk_uniq_str(cx, fmt_sp, ~"")));
|
|
|
|
}
|
|
|
|
stms.push(mk_stmt(cx, fmt_sp,
|
|
|
|
make_new_conv(cx, fmt_sp, conv,
|
|
|
|
args[n], buf())));
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2013-03-20 22:33:10 -05:00
|
|
|
}
|
|
|
|
|
2011-08-27 23:27:39 -05:00
|
|
|
let expected_nargs = n + 1u; // n conversions + the fmt string
|
|
|
|
if expected_nargs < nargs {
|
2011-09-12 05:39:38 -05:00
|
|
|
cx.span_fatal
|
2012-10-12 14:32:36 -05:00
|
|
|
(sp, fmt!("too many arguments to fmt!. found %u, expected %u",
|
2012-08-22 19:24:52 -05:00
|
|
|
nargs, expected_nargs));
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-06-28 18:23:12 -05:00
|
|
|
|
2013-03-20 12:01:58 -05:00
|
|
|
return mk_block(cx, fmt_sp, ~[], stms, Some(buf()));
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|