rust/src/libsyntax_ext/format.rs

881 lines
34 KiB
Rust
Raw Normal View History

// 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.
use self::ArgumentType::*;
use self::Position::*;
use fmt_macros as parse;
use syntax::ast;
use syntax::ext::base::*;
use syntax::ext::base;
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax_pos::{Span, DUMMY_SP};
use syntax::tokenstream;
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
#[derive(PartialEq)]
enum ArgumentType {
Placeholder(String),
Count,
}
2013-12-25 21:55:05 -08:00
enum Position {
2015-01-17 23:33:05 +00:00
Exact(usize),
Named(String),
2013-12-25 21:55:05 -08:00
}
2016-06-06 20:22:48 +05:30
struct Context<'a, 'b: 'a> {
2014-03-09 16:54:34 +02:00
ecx: &'a mut ExtCtxt<'b>,
/// The macro's call site. References to unstable formatting internals must
/// use this span to pass the stability checker.
macsp: Span,
/// The span of the format string literal.
fmtsp: Span,
/// List of parsed argument expressions.
/// Named expressions are resolved early, and are appended to the end of
/// argument expressions.
///
/// Example showing the various data structures in motion:
///
/// * Original: `"{foo:o} {:o} {foo:x} {0:x} {1:o} {:x} {1:x} {0:o}"`
/// * Implicit argument resolution: `"{foo:o} {0:o} {foo:x} {0:x} {1:o} {1:x} {1:x} {0:o}"`
/// * Name resolution: `"{2:o} {0:o} {2:x} {0:x} {1:o} {1:x} {1:x} {0:o}"`
/// * `arg_types` (in JSON): `[[0, 1, 0], [0, 1, 1], [0, 1]]`
/// * `arg_unique_types` (in simplified JSON): `[["o", "x"], ["o", "x"], ["o", "x"]]`
/// * `names` (in JSON): `{"foo": 2}`
2014-09-13 19:06:01 +03:00
args: Vec<P<ast::Expr>>,
/// Placeholder slot numbers indexed by argument.
arg_types: Vec<Vec<usize>>,
/// Unique format specs seen for each argument.
arg_unique_types: Vec<Vec<ArgumentType>>,
/// Map from named arguments to their resolved indices.
names: HashMap<String, usize>,
2014-08-21 14:34:00 +01:00
/// The latest consecutive literal strings, or empty if there weren't any.
literal: String,
2014-08-21 14:34:00 +01:00
/// Collection of the compiled `rt::Argument` structures
2014-09-13 19:06:01 +03:00
pieces: Vec<P<ast::Expr>>,
2014-08-21 14:34:00 +01:00
/// Collection of string literals
2014-09-13 19:06:01 +03:00
str_pieces: Vec<P<ast::Expr>>,
/// Stays `true` if all formatting parameters are default (as in "{}{}").
all_pieces_simple: bool,
/// Mapping between positional argument references and indices into the
/// final generated static argument array. We record the starting indices
/// corresponding to each positional argument, and number of references
/// consumed so far for each argument, to facilitate correct `Position`
2018-05-08 16:10:16 +03:00
/// mapping in `build_piece`. In effect this can be seen as a "flattened"
/// version of `arg_unique_types`.
///
/// Again with the example described above in docstring for `args`:
///
/// * `arg_index_map` (in JSON): `[[0, 1, 0], [2, 3, 3], [4, 5]]`
arg_index_map: Vec<Vec<usize>>,
/// Starting offset of count argument slots.
count_args_index_offset: usize,
/// Count argument slots and tracking data structures.
/// Count arguments are separately tracked for de-duplication in case
/// multiple references are made to one argument. For example, in this
/// format string:
///
/// * Original: `"{:.*} {:.foo$} {1:.*} {:.0$}"`
/// * Implicit argument resolution: `"{1:.0$} {2:.foo$} {1:.3$} {4:.0$}"`
/// * Name resolution: `"{1:.0$} {2:.5$} {1:.3$} {4:.0$}"`
/// * `count_positions` (in JSON): `{0: 0, 5: 1, 3: 2}`
/// * `count_args`: `vec![Exact(0), Exact(5), Exact(3)]`
count_args: Vec<Position>,
/// Relative slot numbers for count arguments.
count_positions: HashMap<usize, usize>,
/// Number of count slots assigned.
count_positions_count: usize,
/// Current position of the implicit positional arg pointer, as if it
/// still existed in this phase of processing.
2018-05-08 16:10:16 +03:00
/// Used only for `all_pieces_simple` tracking in `build_piece`.
curarg: usize,
/// Keep track of invalid references to positional arguments
invalid_refs: Vec<usize>,
}
/// 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 return value is:
///
/// ```text
/// Some((fmtstr, parsed arguments, index map for named arguments))
2015-11-03 16:34:11 +00:00
/// ```
2016-06-06 20:22:48 +05:30
fn parse_args(ecx: &mut ExtCtxt,
sp: Span,
tts: &[tokenstream::TokenTree])
-> Option<(P<ast::Expr>, Vec<P<ast::Expr>>, HashMap<String, usize>)> {
let mut args = Vec::<P<ast::Expr>>::new();
let mut names = HashMap::<String, usize>::new();
let mut p = ecx.new_parser_from_tts(tts);
2014-10-27 19:22:52 +11:00
if p.token == token::Eof {
ecx.span_err(sp, "requires at least a format string argument");
return None;
}
let fmtstr = panictry!(p.parse_expr());
let mut named = false;
2014-10-27 19:22:52 +11:00
while p.token != token::Eof {
if !p.eat(&token::Comma) {
ecx.span_err(sp, "expected token: `,`");
return None;
}
2016-06-06 20:22:48 +05:30
if p.token == token::Eof {
break;
} // accept trailing commas
if named || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) {
named = true;
let ident = match p.token {
token::Ident(i, _) => {
p.bump();
i
}
_ if named => {
ecx.span_err(p.span,
"expected ident, positional arguments \
cannot follow named arguments");
return None;
}
_ => {
ecx.span_err(p.span,
2015-01-07 11:58:31 -05:00
&format!("expected ident for named argument, found `{}`",
2016-06-06 20:22:48 +05:30
p.this_token_to_string()));
return None;
}
};
let name: &str = &ident.name.as_str();
2015-02-03 23:31:06 +01:00
panictry!(p.expect(&token::Eq));
let e = panictry!(p.parse_expr());
if let Some(prev) = names.get(name) {
2016-06-06 20:22:48 +05:30
ecx.struct_span_err(e.span, &format!("duplicate argument named `{}`", name))
.span_note(args[*prev].span, "previously here")
.emit();
continue;
}
// Resolve names into slots early.
// Since all the positional args are already seen at this point
// if the input is valid, we can simply append to the positional
// args. And remember the names.
let slot = args.len();
names.insert(name.to_string(), slot);
args.push(e);
} else {
args.push(panictry!(p.parse_expr()));
}
}
Some((fmtstr, args, names))
}
2014-03-09 16:54:34 +02:00
impl<'a, 'b> Context<'a, 'b> {
fn resolve_name_inplace(&self, p: &mut parse::Piece) {
// NOTE: the `unwrap_or` branch is needed in case of invalid format
// arguments, e.g. `format_args!("{foo}")`.
let lookup = |s| *self.names.get(s).unwrap_or(&0);
match *p {
parse::String(_) => {}
parse::NextArgument(ref mut arg) => {
if let parse::ArgumentNamed(s) = arg.position {
arg.position = parse::ArgumentIs(lookup(s));
}
if let parse::CountIsName(s) = arg.format.width {
arg.format.width = parse::CountIsParam(lookup(s));
}
if let parse::CountIsName(s) = arg.format.precision {
arg.format.precision = parse::CountIsParam(lookup(s));
}
}
}
}
/// Verifies one piece of a parse string, and remembers it if valid.
/// All errors are not emitted as fatal so we can continue giving errors
/// about this and possibly other format strings.
fn verify_piece(&mut self, p: &parse::Piece) {
match *p {
2013-11-28 12:22:53 -08:00
parse::String(..) => {}
parse::NextArgument(ref arg) => {
// width/precision first, if they have implicit positional
// parameters it makes more sense to consume them first.
self.verify_count(arg.format.width);
self.verify_count(arg.format.precision);
// argument second, if it's an implicit positional parameter
// it's written second, so it should come after width/precision.
let pos = match arg.position {
parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i),
parse::ArgumentNamed(s) => Named(s.to_string()),
};
let ty = Placeholder(arg.format.ty.to_string());
self.verify_arg_type(pos, ty);
}
}
}
fn verify_count(&mut self, c: parse::Count) {
match c {
2016-06-06 20:22:48 +05:30
parse::CountImplied |
parse::CountIs(..) => {}
parse::CountIsParam(i) => {
self.verify_arg_type(Exact(i), Count);
}
parse::CountIsName(s) => {
self.verify_arg_type(Named(s.to_string()), Count);
}
}
}
fn describe_num_args(&self) -> String {
match self.args.len() {
0 => "no arguments were given".to_string(),
1 => "there is 1 argument".to_string(),
x => format!("there are {} arguments", x),
}
}
/// Handle invalid references to positional arguments. Output different
/// errors for the case where all arguments are positional and for when
/// there are named arguments or numbered positional arguments in the
/// format string.
fn report_invalid_references(&self, numbered_position_args: bool) {
let mut e;
let mut refs: Vec<String> = self.invalid_refs
.iter()
.map(|r| r.to_string())
.collect();
if self.names.is_empty() && !numbered_position_args {
e = self.ecx.mut_span_err(self.fmtsp,
&format!("{} positional argument{} in format string, but {}",
self.pieces.len(),
if self.pieces.len() > 1 { "s" } else { "" },
self.describe_num_args()));
} else {
let arg_list = match refs.len() {
1 => format!("argument {}", refs.pop().unwrap()),
_ => format!("arguments {head} and {tail}",
tail=refs.pop().unwrap(),
head=refs.join(", "))
};
e = self.ecx.mut_span_err(self.fmtsp,
&format!("invalid reference to positional {} ({})",
arg_list,
self.describe_num_args()));
e.note("positional arguments are zero-based");
};
e.emit();
}
/// Actually verifies and tracks a given format placeholder
/// (a.k.a. argument).
2013-12-25 21:55:05 -08:00
fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
match arg {
2013-12-25 21:55:05 -08:00
Exact(arg) => {
if self.args.len() <= arg {
self.invalid_refs.push(arg);
return;
}
match ty {
Placeholder(_) => {
// record every (position, type) combination only once
let ref mut seen_ty = self.arg_unique_types[arg];
let i = match seen_ty.iter().position(|x| *x == ty) {
Some(i) => i,
None => {
let i = seen_ty.len();
seen_ty.push(ty);
i
}
};
self.arg_types[arg].push(i);
}
Count => {
match self.count_positions.entry(arg) {
Entry::Vacant(e) => {
let i = self.count_positions_count;
e.insert(i);
self.count_args.push(Exact(arg));
self.count_positions_count += 1;
}
Entry::Occupied(_) => {}
}
}
}
}
2013-12-25 21:55:05 -08:00
Named(name) => {
let idx = match self.names.get(&name) {
Some(e) => *e,
None => {
2013-09-27 21:01:58 -07:00
let msg = format!("there is no argument named `{}`", name);
self.ecx.span_err(self.fmtsp, &msg[..]);
return;
}
};
// Treat as positional arg.
self.verify_arg_type(Exact(idx), ty)
}
}
}
/// Builds the mapping between format placeholders and argument objects.
fn build_index_map(&mut self) {
// NOTE: Keep the ordering the same as `into_expr`'s expansion would do!
let args_len = self.args.len();
self.arg_index_map.reserve(args_len);
let mut sofar = 0usize;
// Map the arguments
for i in 0..args_len {
let ref arg_types = self.arg_types[i];
let mut arg_offsets = Vec::with_capacity(arg_types.len());
for offset in arg_types {
arg_offsets.push(sofar + *offset);
}
self.arg_index_map.push(arg_offsets);
sofar += self.arg_unique_types[i].len();
}
// Record starting index for counts, which appear just after arguments
self.count_args_index_offset = sofar;
}
2014-09-13 19:06:01 +03:00
fn rtpath(ecx: &ExtCtxt, s: &str) -> Vec<ast::Ident> {
ecx.std_path(&["fmt", "rt", "v1", s])
}
2018-05-08 16:10:16 +03:00
fn build_count(&self, c: parse::Count) -> P<ast::Expr> {
let sp = self.macsp;
let count = |c, arg| {
std: Stabilize the std::fmt module This commit performs a final stabilization pass over the std::fmt module, marking all necessary APIs as stable. One of the more interesting aspects of this module is that it exposes a good deal of its runtime representation to the outside world in order for `format_args!` to be able to construct the format strings. Instead of hacking the compiler to assume that these items are stable, this commit instead lays out a story for the stabilization and evolution of these APIs. There are three primary details used by the `format_args!` macro: 1. `Arguments` - an opaque package of a "compiled format string". This structure is passed around and the `write` function is the source of truth for transforming a compiled format string into a string at runtime. This must be able to be constructed in stable code. 2. `Argument` - an opaque structure representing an argument to a format string. This is *almost* a trait object as it's just a pointer/function pair, but due to the function originating from one of many traits, it's not actually a trait object. Like `Arguments`, this must be constructed from stable code. 3. `fmt::rt` - this module contains the runtime type definitions primarily for the `rt::Argument` structure. Whenever an argument is formatted with nonstandard flags, a corresponding `rt::Argument` is generated describing how the argument is being formatted. This can be used to construct an `Arguments`. The primary interface to `std::fmt` is the `Arguments` structure, and as such this type name is stabilize as-is today. It is expected for libraries to pass around an `Arguments` structure to represent a pending formatted computation. The remaining portions are largely "cruft" which would rather not be stabilized, but due to the stability checks they must be. As a result, almost all pieces have been renamed to represent that they are "version 1" of the formatting representation. The theory is that at a later date if we change the representation of these types we can add new definitions called "version 2" and corresponding constructors for `Arguments`. One of the other remaining large questions about the fmt module were how the pending I/O reform would affect the signatures of methods in the module. Due to [RFC 526][rfc], however, the writers of fmt are now incompatible with the writers of io, so this question has largely been solved. As a result the interfaces are largely stabilized as-is today. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md Specifically, the following changes were made: * The contents of `fmt::rt` were all moved under `fmt::rt::v1` * `fmt::rt` is stable * `fmt::rt::v1` is stable * `Error` is stable * `Writer` is stable * `Writer::write_str` is stable * `Writer::write_fmt` is stable * `Formatter` is stable * `Argument` has been renamed to `ArgumentV1` and is stable * `ArgumentV1::new` is stable * `ArgumentV1::from_uint` is stable * `Arguments::new_v1` is stable (renamed from `new`) * `Arguments::new_v1_formatted` is stable (renamed from `with_placeholders`) * All formatting traits are now stable, as well as the `fmt` method. * `fmt::write` is stable * `fmt::format` is stable * `Formatter::pad_integral` is stable * `Formatter::pad` is stable * `Formatter::write_str` is stable * `Formatter::write_fmt` is stable * Some assorted top level items which were only used by `format_args!` were removed in favor of static functions on `ArgumentV1` as well. * The formatting-flag-accessing methods remain unstable Within the contents of the `fmt::rt::v1` module, the following actions were taken: * Reexports of all enum variants were removed * All prefixes on enum variants were removed * A few miscellaneous enum variants were renamed * Otherwise all structs, fields, and variants were marked stable. In addition to these actions in the `std::fmt` module, many implementations of `Show` and `String` were stabilized as well. In some other modules: * `ToString` is now stable * `ToString::to_string` is now stable * `Vec` no longer implements `fmt::Writer` (this has moved to `String`) This is a breaking change due to all of the changes to the `fmt::rt` module, but this likely will not have much impact on existing programs. Closes #20661 [breaking-change]
2015-01-13 15:42:53 -08:00
let mut path = Context::rtpath(self.ecx, "Count");
path.push(self.ecx.ident_of(c));
match arg {
Some(arg) => self.ecx.expr_call_global(sp, path, vec![arg]),
None => self.ecx.expr_path(self.ecx.path_global(sp, path)),
}
std: Stabilize the std::fmt module This commit performs a final stabilization pass over the std::fmt module, marking all necessary APIs as stable. One of the more interesting aspects of this module is that it exposes a good deal of its runtime representation to the outside world in order for `format_args!` to be able to construct the format strings. Instead of hacking the compiler to assume that these items are stable, this commit instead lays out a story for the stabilization and evolution of these APIs. There are three primary details used by the `format_args!` macro: 1. `Arguments` - an opaque package of a "compiled format string". This structure is passed around and the `write` function is the source of truth for transforming a compiled format string into a string at runtime. This must be able to be constructed in stable code. 2. `Argument` - an opaque structure representing an argument to a format string. This is *almost* a trait object as it's just a pointer/function pair, but due to the function originating from one of many traits, it's not actually a trait object. Like `Arguments`, this must be constructed from stable code. 3. `fmt::rt` - this module contains the runtime type definitions primarily for the `rt::Argument` structure. Whenever an argument is formatted with nonstandard flags, a corresponding `rt::Argument` is generated describing how the argument is being formatted. This can be used to construct an `Arguments`. The primary interface to `std::fmt` is the `Arguments` structure, and as such this type name is stabilize as-is today. It is expected for libraries to pass around an `Arguments` structure to represent a pending formatted computation. The remaining portions are largely "cruft" which would rather not be stabilized, but due to the stability checks they must be. As a result, almost all pieces have been renamed to represent that they are "version 1" of the formatting representation. The theory is that at a later date if we change the representation of these types we can add new definitions called "version 2" and corresponding constructors for `Arguments`. One of the other remaining large questions about the fmt module were how the pending I/O reform would affect the signatures of methods in the module. Due to [RFC 526][rfc], however, the writers of fmt are now incompatible with the writers of io, so this question has largely been solved. As a result the interfaces are largely stabilized as-is today. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md Specifically, the following changes were made: * The contents of `fmt::rt` were all moved under `fmt::rt::v1` * `fmt::rt` is stable * `fmt::rt::v1` is stable * `Error` is stable * `Writer` is stable * `Writer::write_str` is stable * `Writer::write_fmt` is stable * `Formatter` is stable * `Argument` has been renamed to `ArgumentV1` and is stable * `ArgumentV1::new` is stable * `ArgumentV1::from_uint` is stable * `Arguments::new_v1` is stable (renamed from `new`) * `Arguments::new_v1_formatted` is stable (renamed from `with_placeholders`) * All formatting traits are now stable, as well as the `fmt` method. * `fmt::write` is stable * `fmt::format` is stable * `Formatter::pad_integral` is stable * `Formatter::pad` is stable * `Formatter::write_str` is stable * `Formatter::write_fmt` is stable * Some assorted top level items which were only used by `format_args!` were removed in favor of static functions on `ArgumentV1` as well. * The formatting-flag-accessing methods remain unstable Within the contents of the `fmt::rt::v1` module, the following actions were taken: * Reexports of all enum variants were removed * All prefixes on enum variants were removed * A few miscellaneous enum variants were renamed * Otherwise all structs, fields, and variants were marked stable. In addition to these actions in the `std::fmt` module, many implementations of `Show` and `String` were stabilized as well. In some other modules: * `ToString` is now stable * `ToString::to_string` is now stable * `Vec` no longer implements `fmt::Writer` (this has moved to `String`) This is a breaking change due to all of the changes to the `fmt::rt` module, but this likely will not have much impact on existing programs. Closes #20661 [breaking-change]
2015-01-13 15:42:53 -08:00
};
match c {
parse::CountIs(i) => count("Is", Some(self.ecx.expr_usize(sp, i))),
parse::CountIsParam(i) => {
// This needs mapping too, as `i` is referring to a macro
// argument.
let i = match self.count_positions.get(&i) {
Some(&i) => i,
None => 0, // error already emitted elsewhere
};
let i = i + self.count_args_index_offset;
count("Param", Some(self.ecx.expr_usize(sp, i)))
}
std: Stabilize the std::fmt module This commit performs a final stabilization pass over the std::fmt module, marking all necessary APIs as stable. One of the more interesting aspects of this module is that it exposes a good deal of its runtime representation to the outside world in order for `format_args!` to be able to construct the format strings. Instead of hacking the compiler to assume that these items are stable, this commit instead lays out a story for the stabilization and evolution of these APIs. There are three primary details used by the `format_args!` macro: 1. `Arguments` - an opaque package of a "compiled format string". This structure is passed around and the `write` function is the source of truth for transforming a compiled format string into a string at runtime. This must be able to be constructed in stable code. 2. `Argument` - an opaque structure representing an argument to a format string. This is *almost* a trait object as it's just a pointer/function pair, but due to the function originating from one of many traits, it's not actually a trait object. Like `Arguments`, this must be constructed from stable code. 3. `fmt::rt` - this module contains the runtime type definitions primarily for the `rt::Argument` structure. Whenever an argument is formatted with nonstandard flags, a corresponding `rt::Argument` is generated describing how the argument is being formatted. This can be used to construct an `Arguments`. The primary interface to `std::fmt` is the `Arguments` structure, and as such this type name is stabilize as-is today. It is expected for libraries to pass around an `Arguments` structure to represent a pending formatted computation. The remaining portions are largely "cruft" which would rather not be stabilized, but due to the stability checks they must be. As a result, almost all pieces have been renamed to represent that they are "version 1" of the formatting representation. The theory is that at a later date if we change the representation of these types we can add new definitions called "version 2" and corresponding constructors for `Arguments`. One of the other remaining large questions about the fmt module were how the pending I/O reform would affect the signatures of methods in the module. Due to [RFC 526][rfc], however, the writers of fmt are now incompatible with the writers of io, so this question has largely been solved. As a result the interfaces are largely stabilized as-is today. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md Specifically, the following changes were made: * The contents of `fmt::rt` were all moved under `fmt::rt::v1` * `fmt::rt` is stable * `fmt::rt::v1` is stable * `Error` is stable * `Writer` is stable * `Writer::write_str` is stable * `Writer::write_fmt` is stable * `Formatter` is stable * `Argument` has been renamed to `ArgumentV1` and is stable * `ArgumentV1::new` is stable * `ArgumentV1::from_uint` is stable * `Arguments::new_v1` is stable (renamed from `new`) * `Arguments::new_v1_formatted` is stable (renamed from `with_placeholders`) * All formatting traits are now stable, as well as the `fmt` method. * `fmt::write` is stable * `fmt::format` is stable * `Formatter::pad_integral` is stable * `Formatter::pad` is stable * `Formatter::write_str` is stable * `Formatter::write_fmt` is stable * Some assorted top level items which were only used by `format_args!` were removed in favor of static functions on `ArgumentV1` as well. * The formatting-flag-accessing methods remain unstable Within the contents of the `fmt::rt::v1` module, the following actions were taken: * Reexports of all enum variants were removed * All prefixes on enum variants were removed * A few miscellaneous enum variants were renamed * Otherwise all structs, fields, and variants were marked stable. In addition to these actions in the `std::fmt` module, many implementations of `Show` and `String` were stabilized as well. In some other modules: * `ToString` is now stable * `ToString::to_string` is now stable * `Vec` no longer implements `fmt::Writer` (this has moved to `String`) This is a breaking change due to all of the changes to the `fmt::rt` module, but this likely will not have much impact on existing programs. Closes #20661 [breaking-change]
2015-01-13 15:42:53 -08:00
parse::CountImplied => count("Implied", None),
// should never be the case, names are already resolved
parse::CountIsName(_) => panic!("should never happen"),
}
}
2018-05-08 16:10:16 +03:00
/// Build a literal expression from the accumulated string literals
fn build_literal_string(&mut self) -> P<ast::Expr> {
let sp = self.fmtsp;
let s = Symbol::intern(&self.literal);
2014-08-21 14:34:00 +01:00
self.literal.clear();
self.ecx.expr_str(sp, s)
}
2018-05-08 16:10:16 +03:00
/// Build a static `rt::Argument` from a `parse::Piece` or append
2014-08-21 14:34:00 +01:00
/// to the `literal` string.
2018-05-08 16:10:16 +03:00
fn build_piece(&mut self,
piece: &parse::Piece,
arg_index_consumed: &mut Vec<usize>)
-> Option<P<ast::Expr>> {
let sp = self.macsp;
match *piece {
parse::String(s) => {
2014-08-21 14:34:00 +01:00
self.literal.push_str(s);
None
}
parse::NextArgument(ref arg) => {
2018-05-08 16:10:16 +03:00
// Build the position
std: Stabilize the std::fmt module This commit performs a final stabilization pass over the std::fmt module, marking all necessary APIs as stable. One of the more interesting aspects of this module is that it exposes a good deal of its runtime representation to the outside world in order for `format_args!` to be able to construct the format strings. Instead of hacking the compiler to assume that these items are stable, this commit instead lays out a story for the stabilization and evolution of these APIs. There are three primary details used by the `format_args!` macro: 1. `Arguments` - an opaque package of a "compiled format string". This structure is passed around and the `write` function is the source of truth for transforming a compiled format string into a string at runtime. This must be able to be constructed in stable code. 2. `Argument` - an opaque structure representing an argument to a format string. This is *almost* a trait object as it's just a pointer/function pair, but due to the function originating from one of many traits, it's not actually a trait object. Like `Arguments`, this must be constructed from stable code. 3. `fmt::rt` - this module contains the runtime type definitions primarily for the `rt::Argument` structure. Whenever an argument is formatted with nonstandard flags, a corresponding `rt::Argument` is generated describing how the argument is being formatted. This can be used to construct an `Arguments`. The primary interface to `std::fmt` is the `Arguments` structure, and as such this type name is stabilize as-is today. It is expected for libraries to pass around an `Arguments` structure to represent a pending formatted computation. The remaining portions are largely "cruft" which would rather not be stabilized, but due to the stability checks they must be. As a result, almost all pieces have been renamed to represent that they are "version 1" of the formatting representation. The theory is that at a later date if we change the representation of these types we can add new definitions called "version 2" and corresponding constructors for `Arguments`. One of the other remaining large questions about the fmt module were how the pending I/O reform would affect the signatures of methods in the module. Due to [RFC 526][rfc], however, the writers of fmt are now incompatible with the writers of io, so this question has largely been solved. As a result the interfaces are largely stabilized as-is today. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md Specifically, the following changes were made: * The contents of `fmt::rt` were all moved under `fmt::rt::v1` * `fmt::rt` is stable * `fmt::rt::v1` is stable * `Error` is stable * `Writer` is stable * `Writer::write_str` is stable * `Writer::write_fmt` is stable * `Formatter` is stable * `Argument` has been renamed to `ArgumentV1` and is stable * `ArgumentV1::new` is stable * `ArgumentV1::from_uint` is stable * `Arguments::new_v1` is stable (renamed from `new`) * `Arguments::new_v1_formatted` is stable (renamed from `with_placeholders`) * All formatting traits are now stable, as well as the `fmt` method. * `fmt::write` is stable * `fmt::format` is stable * `Formatter::pad_integral` is stable * `Formatter::pad` is stable * `Formatter::write_str` is stable * `Formatter::write_fmt` is stable * Some assorted top level items which were only used by `format_args!` were removed in favor of static functions on `ArgumentV1` as well. * The formatting-flag-accessing methods remain unstable Within the contents of the `fmt::rt::v1` module, the following actions were taken: * Reexports of all enum variants were removed * All prefixes on enum variants were removed * A few miscellaneous enum variants were renamed * Otherwise all structs, fields, and variants were marked stable. In addition to these actions in the `std::fmt` module, many implementations of `Show` and `String` were stabilized as well. In some other modules: * `ToString` is now stable * `ToString::to_string` is now stable * `Vec` no longer implements `fmt::Writer` (this has moved to `String`) This is a breaking change due to all of the changes to the `fmt::rt` module, but this likely will not have much impact on existing programs. Closes #20661 [breaking-change]
2015-01-13 15:42:53 -08:00
let pos = {
let pos = |c, arg| {
std: Stabilize the std::fmt module This commit performs a final stabilization pass over the std::fmt module, marking all necessary APIs as stable. One of the more interesting aspects of this module is that it exposes a good deal of its runtime representation to the outside world in order for `format_args!` to be able to construct the format strings. Instead of hacking the compiler to assume that these items are stable, this commit instead lays out a story for the stabilization and evolution of these APIs. There are three primary details used by the `format_args!` macro: 1. `Arguments` - an opaque package of a "compiled format string". This structure is passed around and the `write` function is the source of truth for transforming a compiled format string into a string at runtime. This must be able to be constructed in stable code. 2. `Argument` - an opaque structure representing an argument to a format string. This is *almost* a trait object as it's just a pointer/function pair, but due to the function originating from one of many traits, it's not actually a trait object. Like `Arguments`, this must be constructed from stable code. 3. `fmt::rt` - this module contains the runtime type definitions primarily for the `rt::Argument` structure. Whenever an argument is formatted with nonstandard flags, a corresponding `rt::Argument` is generated describing how the argument is being formatted. This can be used to construct an `Arguments`. The primary interface to `std::fmt` is the `Arguments` structure, and as such this type name is stabilize as-is today. It is expected for libraries to pass around an `Arguments` structure to represent a pending formatted computation. The remaining portions are largely "cruft" which would rather not be stabilized, but due to the stability checks they must be. As a result, almost all pieces have been renamed to represent that they are "version 1" of the formatting representation. The theory is that at a later date if we change the representation of these types we can add new definitions called "version 2" and corresponding constructors for `Arguments`. One of the other remaining large questions about the fmt module were how the pending I/O reform would affect the signatures of methods in the module. Due to [RFC 526][rfc], however, the writers of fmt are now incompatible with the writers of io, so this question has largely been solved. As a result the interfaces are largely stabilized as-is today. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md Specifically, the following changes were made: * The contents of `fmt::rt` were all moved under `fmt::rt::v1` * `fmt::rt` is stable * `fmt::rt::v1` is stable * `Error` is stable * `Writer` is stable * `Writer::write_str` is stable * `Writer::write_fmt` is stable * `Formatter` is stable * `Argument` has been renamed to `ArgumentV1` and is stable * `ArgumentV1::new` is stable * `ArgumentV1::from_uint` is stable * `Arguments::new_v1` is stable (renamed from `new`) * `Arguments::new_v1_formatted` is stable (renamed from `with_placeholders`) * All formatting traits are now stable, as well as the `fmt` method. * `fmt::write` is stable * `fmt::format` is stable * `Formatter::pad_integral` is stable * `Formatter::pad` is stable * `Formatter::write_str` is stable * `Formatter::write_fmt` is stable * Some assorted top level items which were only used by `format_args!` were removed in favor of static functions on `ArgumentV1` as well. * The formatting-flag-accessing methods remain unstable Within the contents of the `fmt::rt::v1` module, the following actions were taken: * Reexports of all enum variants were removed * All prefixes on enum variants were removed * A few miscellaneous enum variants were renamed * Otherwise all structs, fields, and variants were marked stable. In addition to these actions in the `std::fmt` module, many implementations of `Show` and `String` were stabilized as well. In some other modules: * `ToString` is now stable * `ToString::to_string` is now stable * `Vec` no longer implements `fmt::Writer` (this has moved to `String`) This is a breaking change due to all of the changes to the `fmt::rt` module, but this likely will not have much impact on existing programs. Closes #20661 [breaking-change]
2015-01-13 15:42:53 -08:00
let mut path = Context::rtpath(self.ecx, "Position");
path.push(self.ecx.ident_of(c));
match arg {
Some(i) => {
let arg = self.ecx.expr_usize(sp, i);
self.ecx.expr_call_global(sp, path, vec![arg])
}
2016-06-06 20:22:48 +05:30
None => self.ecx.expr_path(self.ecx.path_global(sp, path)),
std: Stabilize the std::fmt module This commit performs a final stabilization pass over the std::fmt module, marking all necessary APIs as stable. One of the more interesting aspects of this module is that it exposes a good deal of its runtime representation to the outside world in order for `format_args!` to be able to construct the format strings. Instead of hacking the compiler to assume that these items are stable, this commit instead lays out a story for the stabilization and evolution of these APIs. There are three primary details used by the `format_args!` macro: 1. `Arguments` - an opaque package of a "compiled format string". This structure is passed around and the `write` function is the source of truth for transforming a compiled format string into a string at runtime. This must be able to be constructed in stable code. 2. `Argument` - an opaque structure representing an argument to a format string. This is *almost* a trait object as it's just a pointer/function pair, but due to the function originating from one of many traits, it's not actually a trait object. Like `Arguments`, this must be constructed from stable code. 3. `fmt::rt` - this module contains the runtime type definitions primarily for the `rt::Argument` structure. Whenever an argument is formatted with nonstandard flags, a corresponding `rt::Argument` is generated describing how the argument is being formatted. This can be used to construct an `Arguments`. The primary interface to `std::fmt` is the `Arguments` structure, and as such this type name is stabilize as-is today. It is expected for libraries to pass around an `Arguments` structure to represent a pending formatted computation. The remaining portions are largely "cruft" which would rather not be stabilized, but due to the stability checks they must be. As a result, almost all pieces have been renamed to represent that they are "version 1" of the formatting representation. The theory is that at a later date if we change the representation of these types we can add new definitions called "version 2" and corresponding constructors for `Arguments`. One of the other remaining large questions about the fmt module were how the pending I/O reform would affect the signatures of methods in the module. Due to [RFC 526][rfc], however, the writers of fmt are now incompatible with the writers of io, so this question has largely been solved. As a result the interfaces are largely stabilized as-is today. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md Specifically, the following changes were made: * The contents of `fmt::rt` were all moved under `fmt::rt::v1` * `fmt::rt` is stable * `fmt::rt::v1` is stable * `Error` is stable * `Writer` is stable * `Writer::write_str` is stable * `Writer::write_fmt` is stable * `Formatter` is stable * `Argument` has been renamed to `ArgumentV1` and is stable * `ArgumentV1::new` is stable * `ArgumentV1::from_uint` is stable * `Arguments::new_v1` is stable (renamed from `new`) * `Arguments::new_v1_formatted` is stable (renamed from `with_placeholders`) * All formatting traits are now stable, as well as the `fmt` method. * `fmt::write` is stable * `fmt::format` is stable * `Formatter::pad_integral` is stable * `Formatter::pad` is stable * `Formatter::write_str` is stable * `Formatter::write_fmt` is stable * Some assorted top level items which were only used by `format_args!` were removed in favor of static functions on `ArgumentV1` as well. * The formatting-flag-accessing methods remain unstable Within the contents of the `fmt::rt::v1` module, the following actions were taken: * Reexports of all enum variants were removed * All prefixes on enum variants were removed * A few miscellaneous enum variants were renamed * Otherwise all structs, fields, and variants were marked stable. In addition to these actions in the `std::fmt` module, many implementations of `Show` and `String` were stabilized as well. In some other modules: * `ToString` is now stable * `ToString::to_string` is now stable * `Vec` no longer implements `fmt::Writer` (this has moved to `String`) This is a breaking change due to all of the changes to the `fmt::rt` module, but this likely will not have much impact on existing programs. Closes #20661 [breaking-change]
2015-01-13 15:42:53 -08:00
}
};
match arg.position {
parse::ArgumentIs(i)
| parse::ArgumentImplicitlyIs(i) => {
// Map to index in final generated argument array
// in case of multiple types specified
let arg_idx = match arg_index_consumed.get_mut(i) {
None => 0, // error already emitted elsewhere
Some(offset) => {
let ref idx_map = self.arg_index_map[i];
// unwrap_or branch: error already emitted elsewhere
let arg_idx = *idx_map.get(*offset).unwrap_or(&0);
*offset += 1;
arg_idx
}
};
pos("At", Some(arg_idx))
}
std: Stabilize the std::fmt module This commit performs a final stabilization pass over the std::fmt module, marking all necessary APIs as stable. One of the more interesting aspects of this module is that it exposes a good deal of its runtime representation to the outside world in order for `format_args!` to be able to construct the format strings. Instead of hacking the compiler to assume that these items are stable, this commit instead lays out a story for the stabilization and evolution of these APIs. There are three primary details used by the `format_args!` macro: 1. `Arguments` - an opaque package of a "compiled format string". This structure is passed around and the `write` function is the source of truth for transforming a compiled format string into a string at runtime. This must be able to be constructed in stable code. 2. `Argument` - an opaque structure representing an argument to a format string. This is *almost* a trait object as it's just a pointer/function pair, but due to the function originating from one of many traits, it's not actually a trait object. Like `Arguments`, this must be constructed from stable code. 3. `fmt::rt` - this module contains the runtime type definitions primarily for the `rt::Argument` structure. Whenever an argument is formatted with nonstandard flags, a corresponding `rt::Argument` is generated describing how the argument is being formatted. This can be used to construct an `Arguments`. The primary interface to `std::fmt` is the `Arguments` structure, and as such this type name is stabilize as-is today. It is expected for libraries to pass around an `Arguments` structure to represent a pending formatted computation. The remaining portions are largely "cruft" which would rather not be stabilized, but due to the stability checks they must be. As a result, almost all pieces have been renamed to represent that they are "version 1" of the formatting representation. The theory is that at a later date if we change the representation of these types we can add new definitions called "version 2" and corresponding constructors for `Arguments`. One of the other remaining large questions about the fmt module were how the pending I/O reform would affect the signatures of methods in the module. Due to [RFC 526][rfc], however, the writers of fmt are now incompatible with the writers of io, so this question has largely been solved. As a result the interfaces are largely stabilized as-is today. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md Specifically, the following changes were made: * The contents of `fmt::rt` were all moved under `fmt::rt::v1` * `fmt::rt` is stable * `fmt::rt::v1` is stable * `Error` is stable * `Writer` is stable * `Writer::write_str` is stable * `Writer::write_fmt` is stable * `Formatter` is stable * `Argument` has been renamed to `ArgumentV1` and is stable * `ArgumentV1::new` is stable * `ArgumentV1::from_uint` is stable * `Arguments::new_v1` is stable (renamed from `new`) * `Arguments::new_v1_formatted` is stable (renamed from `with_placeholders`) * All formatting traits are now stable, as well as the `fmt` method. * `fmt::write` is stable * `fmt::format` is stable * `Formatter::pad_integral` is stable * `Formatter::pad` is stable * `Formatter::write_str` is stable * `Formatter::write_fmt` is stable * Some assorted top level items which were only used by `format_args!` were removed in favor of static functions on `ArgumentV1` as well. * The formatting-flag-accessing methods remain unstable Within the contents of the `fmt::rt::v1` module, the following actions were taken: * Reexports of all enum variants were removed * All prefixes on enum variants were removed * A few miscellaneous enum variants were renamed * Otherwise all structs, fields, and variants were marked stable. In addition to these actions in the `std::fmt` module, many implementations of `Show` and `String` were stabilized as well. In some other modules: * `ToString` is now stable * `ToString::to_string` is now stable * `Vec` no longer implements `fmt::Writer` (this has moved to `String`) This is a breaking change due to all of the changes to the `fmt::rt` module, but this likely will not have much impact on existing programs. Closes #20661 [breaking-change]
2015-01-13 15:42:53 -08:00
// should never be the case, because names are already
// resolved.
parse::ArgumentNamed(_) => panic!("should never happen"),
}
};
let simple_arg = parse::Argument {
position: {
// We don't have ArgumentNext any more, so we have to
// track the current argument ourselves.
let i = self.curarg;
self.curarg += 1;
parse::ArgumentIs(i)
},
format: parse::FormatSpec {
fill: arg.format.fill,
align: parse::AlignUnknown,
flags: 0,
precision: parse::CountImplied,
width: parse::CountImplied,
2016-06-06 20:22:48 +05:30
ty: arg.format.ty,
},
};
2016-06-06 20:22:48 +05:30
let fill = match arg.format.fill {
Some(c) => c,
None => ' ',
};
if *arg != simple_arg || fill != ' ' {
self.all_pieces_simple = false;
}
2018-05-08 16:10:16 +03:00
// Build the format
let fill = self.ecx.expr_lit(sp, ast::LitKind::Char(fill));
let align = |name| {
std: Stabilize the std::fmt module This commit performs a final stabilization pass over the std::fmt module, marking all necessary APIs as stable. One of the more interesting aspects of this module is that it exposes a good deal of its runtime representation to the outside world in order for `format_args!` to be able to construct the format strings. Instead of hacking the compiler to assume that these items are stable, this commit instead lays out a story for the stabilization and evolution of these APIs. There are three primary details used by the `format_args!` macro: 1. `Arguments` - an opaque package of a "compiled format string". This structure is passed around and the `write` function is the source of truth for transforming a compiled format string into a string at runtime. This must be able to be constructed in stable code. 2. `Argument` - an opaque structure representing an argument to a format string. This is *almost* a trait object as it's just a pointer/function pair, but due to the function originating from one of many traits, it's not actually a trait object. Like `Arguments`, this must be constructed from stable code. 3. `fmt::rt` - this module contains the runtime type definitions primarily for the `rt::Argument` structure. Whenever an argument is formatted with nonstandard flags, a corresponding `rt::Argument` is generated describing how the argument is being formatted. This can be used to construct an `Arguments`. The primary interface to `std::fmt` is the `Arguments` structure, and as such this type name is stabilize as-is today. It is expected for libraries to pass around an `Arguments` structure to represent a pending formatted computation. The remaining portions are largely "cruft" which would rather not be stabilized, but due to the stability checks they must be. As a result, almost all pieces have been renamed to represent that they are "version 1" of the formatting representation. The theory is that at a later date if we change the representation of these types we can add new definitions called "version 2" and corresponding constructors for `Arguments`. One of the other remaining large questions about the fmt module were how the pending I/O reform would affect the signatures of methods in the module. Due to [RFC 526][rfc], however, the writers of fmt are now incompatible with the writers of io, so this question has largely been solved. As a result the interfaces are largely stabilized as-is today. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md Specifically, the following changes were made: * The contents of `fmt::rt` were all moved under `fmt::rt::v1` * `fmt::rt` is stable * `fmt::rt::v1` is stable * `Error` is stable * `Writer` is stable * `Writer::write_str` is stable * `Writer::write_fmt` is stable * `Formatter` is stable * `Argument` has been renamed to `ArgumentV1` and is stable * `ArgumentV1::new` is stable * `ArgumentV1::from_uint` is stable * `Arguments::new_v1` is stable (renamed from `new`) * `Arguments::new_v1_formatted` is stable (renamed from `with_placeholders`) * All formatting traits are now stable, as well as the `fmt` method. * `fmt::write` is stable * `fmt::format` is stable * `Formatter::pad_integral` is stable * `Formatter::pad` is stable * `Formatter::write_str` is stable * `Formatter::write_fmt` is stable * Some assorted top level items which were only used by `format_args!` were removed in favor of static functions on `ArgumentV1` as well. * The formatting-flag-accessing methods remain unstable Within the contents of the `fmt::rt::v1` module, the following actions were taken: * Reexports of all enum variants were removed * All prefixes on enum variants were removed * A few miscellaneous enum variants were renamed * Otherwise all structs, fields, and variants were marked stable. In addition to these actions in the `std::fmt` module, many implementations of `Show` and `String` were stabilized as well. In some other modules: * `ToString` is now stable * `ToString::to_string` is now stable * `Vec` no longer implements `fmt::Writer` (this has moved to `String`) This is a breaking change due to all of the changes to the `fmt::rt` module, but this likely will not have much impact on existing programs. Closes #20661 [breaking-change]
2015-01-13 15:42:53 -08:00
let mut p = Context::rtpath(self.ecx, "Alignment");
p.push(self.ecx.ident_of(name));
self.ecx.path_global(sp, p)
};
let align = match arg.format.align {
std: Stabilize the std::fmt module This commit performs a final stabilization pass over the std::fmt module, marking all necessary APIs as stable. One of the more interesting aspects of this module is that it exposes a good deal of its runtime representation to the outside world in order for `format_args!` to be able to construct the format strings. Instead of hacking the compiler to assume that these items are stable, this commit instead lays out a story for the stabilization and evolution of these APIs. There are three primary details used by the `format_args!` macro: 1. `Arguments` - an opaque package of a "compiled format string". This structure is passed around and the `write` function is the source of truth for transforming a compiled format string into a string at runtime. This must be able to be constructed in stable code. 2. `Argument` - an opaque structure representing an argument to a format string. This is *almost* a trait object as it's just a pointer/function pair, but due to the function originating from one of many traits, it's not actually a trait object. Like `Arguments`, this must be constructed from stable code. 3. `fmt::rt` - this module contains the runtime type definitions primarily for the `rt::Argument` structure. Whenever an argument is formatted with nonstandard flags, a corresponding `rt::Argument` is generated describing how the argument is being formatted. This can be used to construct an `Arguments`. The primary interface to `std::fmt` is the `Arguments` structure, and as such this type name is stabilize as-is today. It is expected for libraries to pass around an `Arguments` structure to represent a pending formatted computation. The remaining portions are largely "cruft" which would rather not be stabilized, but due to the stability checks they must be. As a result, almost all pieces have been renamed to represent that they are "version 1" of the formatting representation. The theory is that at a later date if we change the representation of these types we can add new definitions called "version 2" and corresponding constructors for `Arguments`. One of the other remaining large questions about the fmt module were how the pending I/O reform would affect the signatures of methods in the module. Due to [RFC 526][rfc], however, the writers of fmt are now incompatible with the writers of io, so this question has largely been solved. As a result the interfaces are largely stabilized as-is today. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md Specifically, the following changes were made: * The contents of `fmt::rt` were all moved under `fmt::rt::v1` * `fmt::rt` is stable * `fmt::rt::v1` is stable * `Error` is stable * `Writer` is stable * `Writer::write_str` is stable * `Writer::write_fmt` is stable * `Formatter` is stable * `Argument` has been renamed to `ArgumentV1` and is stable * `ArgumentV1::new` is stable * `ArgumentV1::from_uint` is stable * `Arguments::new_v1` is stable (renamed from `new`) * `Arguments::new_v1_formatted` is stable (renamed from `with_placeholders`) * All formatting traits are now stable, as well as the `fmt` method. * `fmt::write` is stable * `fmt::format` is stable * `Formatter::pad_integral` is stable * `Formatter::pad` is stable * `Formatter::write_str` is stable * `Formatter::write_fmt` is stable * Some assorted top level items which were only used by `format_args!` were removed in favor of static functions on `ArgumentV1` as well. * The formatting-flag-accessing methods remain unstable Within the contents of the `fmt::rt::v1` module, the following actions were taken: * Reexports of all enum variants were removed * All prefixes on enum variants were removed * A few miscellaneous enum variants were renamed * Otherwise all structs, fields, and variants were marked stable. In addition to these actions in the `std::fmt` module, many implementations of `Show` and `String` were stabilized as well. In some other modules: * `ToString` is now stable * `ToString::to_string` is now stable * `Vec` no longer implements `fmt::Writer` (this has moved to `String`) This is a breaking change due to all of the changes to the `fmt::rt` module, but this likely will not have much impact on existing programs. Closes #20661 [breaking-change]
2015-01-13 15:42:53 -08:00
parse::AlignLeft => align("Left"),
parse::AlignRight => align("Right"),
parse::AlignCenter => align("Center"),
parse::AlignUnknown => align("Unknown"),
};
let align = self.ecx.expr_path(align);
2015-02-23 16:07:38 +13:00
let flags = self.ecx.expr_u32(sp, arg.format.flags);
2018-05-08 16:10:16 +03:00
let prec = self.build_count(arg.format.precision);
let width = self.build_count(arg.format.width);
2014-09-13 19:06:01 +03:00
let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec"));
2016-06-06 20:22:48 +05:30
let fmt =
self.ecx.expr_struct(sp,
path,
vec![self.ecx
.field_imm(sp, self.ecx.ident_of("fill"), fill),
self.ecx.field_imm(sp,
self.ecx.ident_of("align"),
align),
self.ecx.field_imm(sp,
self.ecx.ident_of("flags"),
flags),
self.ecx.field_imm(sp,
self.ecx.ident_of("precision"),
prec),
self.ecx.field_imm(sp,
self.ecx.ident_of("width"),
width)]);
2014-09-13 19:06:01 +03:00
let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "Argument"));
2016-06-06 20:22:48 +05:30
Some(self.ecx.expr_struct(sp,
path,
vec![self.ecx.field_imm(sp,
self.ecx.ident_of("position"),
pos),
self.ecx.field_imm(sp,
self.ecx.ident_of("format"),
fmt)]))
}
}
}
/// Actually builds the expression which the format_args! block will be
/// expanded to
fn into_expr(self) -> P<ast::Expr> {
let mut locals = Vec::new();
let mut counts = Vec::new();
let mut pats = Vec::new();
let mut heads = Vec::new();
2018-04-24 21:30:13 +01:00
let names_pos: Vec<_> = (0..self.args.len()).map(|i| {
self.ecx.ident_of(&format!("arg{}", i)).gensym()
}).collect();
// First, build up the static array which will become our precompiled
// format "string"
let pieces = self.ecx.expr_vec_slice(self.fmtsp, self.str_pieces);
// Before consuming the expressions, we have to remember spans for
// count arguments as they are now generated separate from other
// arguments, hence have no access to the `P<ast::Expr>`'s.
let spans_pos: Vec<_> = self.args.iter().map(|e| e.span.clone()).collect();
// Right now there is a bug such that for the expression:
// foo(bar(&1))
// the lifetime of `1` doesn't outlast the call to `bar`, so it's not
// valid for the call to `foo`. To work around this all arguments to the
2013-09-27 21:01:58 -07:00
// format! string are shoved into locals. Furthermore, we shove the address
// of each variable because we don't want to move out of the arguments
// passed to this function.
2014-09-14 20:27:36 -07:00
for (i, e) in self.args.into_iter().enumerate() {
2018-04-24 21:30:13 +01:00
let name = names_pos[i];
2017-07-31 23:04:34 +03:00
let span =
DUMMY_SP.with_ctxt(e.span.ctxt().apply_mark(self.ecx.current_expansion.mark));
pats.push(self.ecx.pat_ident(span, name));
for ref arg_ty in self.arg_unique_types[i].iter() {
locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, name));
}
heads.push(self.ecx.expr_addr_of(e.span, e));
}
for pos in self.count_args {
2018-04-24 21:30:13 +01:00
let index = match pos {
Exact(i) => i,
_ => panic!("should never happen"),
};
2018-04-24 21:30:13 +01:00
let name = names_pos[index];
let span = spans_pos[index];
counts.push(Context::format_arg(self.ecx, self.macsp, span, &Count, name));
}
// Now create a vector containing all the arguments
let args = locals.into_iter().chain(counts.into_iter());
let args_array = self.ecx.expr_vec(self.fmtsp, args.collect());
// Constructs an AST equivalent to:
//
// match (&arg0, &arg1) {
// (tmp0, tmp1) => args_array
// }
//
// It was:
//
// let tmp0 = &arg0;
// let tmp1 = &arg1;
// 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 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 => 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);
2016-06-06 20:22:48 +05:30
let arm = self.ecx.arm(self.fmtsp, vec![pat], args_array);
let head = self.ecx.expr(self.fmtsp, ast::ExprKind::Tup(heads));
2016-06-06 20:22:48 +05:30
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 {
std: Stabilize the std::fmt module This commit performs a final stabilization pass over the std::fmt module, marking all necessary APIs as stable. One of the more interesting aspects of this module is that it exposes a good deal of its runtime representation to the outside world in order for `format_args!` to be able to construct the format strings. Instead of hacking the compiler to assume that these items are stable, this commit instead lays out a story for the stabilization and evolution of these APIs. There are three primary details used by the `format_args!` macro: 1. `Arguments` - an opaque package of a "compiled format string". This structure is passed around and the `write` function is the source of truth for transforming a compiled format string into a string at runtime. This must be able to be constructed in stable code. 2. `Argument` - an opaque structure representing an argument to a format string. This is *almost* a trait object as it's just a pointer/function pair, but due to the function originating from one of many traits, it's not actually a trait object. Like `Arguments`, this must be constructed from stable code. 3. `fmt::rt` - this module contains the runtime type definitions primarily for the `rt::Argument` structure. Whenever an argument is formatted with nonstandard flags, a corresponding `rt::Argument` is generated describing how the argument is being formatted. This can be used to construct an `Arguments`. The primary interface to `std::fmt` is the `Arguments` structure, and as such this type name is stabilize as-is today. It is expected for libraries to pass around an `Arguments` structure to represent a pending formatted computation. The remaining portions are largely "cruft" which would rather not be stabilized, but due to the stability checks they must be. As a result, almost all pieces have been renamed to represent that they are "version 1" of the formatting representation. The theory is that at a later date if we change the representation of these types we can add new definitions called "version 2" and corresponding constructors for `Arguments`. One of the other remaining large questions about the fmt module were how the pending I/O reform would affect the signatures of methods in the module. Due to [RFC 526][rfc], however, the writers of fmt are now incompatible with the writers of io, so this question has largely been solved. As a result the interfaces are largely stabilized as-is today. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md Specifically, the following changes were made: * The contents of `fmt::rt` were all moved under `fmt::rt::v1` * `fmt::rt` is stable * `fmt::rt::v1` is stable * `Error` is stable * `Writer` is stable * `Writer::write_str` is stable * `Writer::write_fmt` is stable * `Formatter` is stable * `Argument` has been renamed to `ArgumentV1` and is stable * `ArgumentV1::new` is stable * `ArgumentV1::from_uint` is stable * `Arguments::new_v1` is stable (renamed from `new`) * `Arguments::new_v1_formatted` is stable (renamed from `with_placeholders`) * All formatting traits are now stable, as well as the `fmt` method. * `fmt::write` is stable * `fmt::format` is stable * `Formatter::pad_integral` is stable * `Formatter::pad` is stable * `Formatter::write_str` is stable * `Formatter::write_fmt` is stable * Some assorted top level items which were only used by `format_args!` were removed in favor of static functions on `ArgumentV1` as well. * The formatting-flag-accessing methods remain unstable Within the contents of the `fmt::rt::v1` module, the following actions were taken: * Reexports of all enum variants were removed * All prefixes on enum variants were removed * A few miscellaneous enum variants were renamed * Otherwise all structs, fields, and variants were marked stable. In addition to these actions in the `std::fmt` module, many implementations of `Show` and `String` were stabilized as well. In some other modules: * `ToString` is now stable * `ToString::to_string` is now stable * `Vec` no longer implements `fmt::Writer` (this has moved to `String`) This is a breaking change due to all of the changes to the `fmt::rt` module, but this likely will not have much impact on existing programs. Closes #20661 [breaking-change]
2015-01-13 15:42:53 -08:00
("new_v1", vec![pieces, args_slice])
} else {
// Build up the static array which will store our precompiled
// nonstandard placeholders, if there are any.
let fmt = self.ecx.expr_vec_slice(self.macsp, self.pieces);
std: Stabilize the std::fmt module This commit performs a final stabilization pass over the std::fmt module, marking all necessary APIs as stable. One of the more interesting aspects of this module is that it exposes a good deal of its runtime representation to the outside world in order for `format_args!` to be able to construct the format strings. Instead of hacking the compiler to assume that these items are stable, this commit instead lays out a story for the stabilization and evolution of these APIs. There are three primary details used by the `format_args!` macro: 1. `Arguments` - an opaque package of a "compiled format string". This structure is passed around and the `write` function is the source of truth for transforming a compiled format string into a string at runtime. This must be able to be constructed in stable code. 2. `Argument` - an opaque structure representing an argument to a format string. This is *almost* a trait object as it's just a pointer/function pair, but due to the function originating from one of many traits, it's not actually a trait object. Like `Arguments`, this must be constructed from stable code. 3. `fmt::rt` - this module contains the runtime type definitions primarily for the `rt::Argument` structure. Whenever an argument is formatted with nonstandard flags, a corresponding `rt::Argument` is generated describing how the argument is being formatted. This can be used to construct an `Arguments`. The primary interface to `std::fmt` is the `Arguments` structure, and as such this type name is stabilize as-is today. It is expected for libraries to pass around an `Arguments` structure to represent a pending formatted computation. The remaining portions are largely "cruft" which would rather not be stabilized, but due to the stability checks they must be. As a result, almost all pieces have been renamed to represent that they are "version 1" of the formatting representation. The theory is that at a later date if we change the representation of these types we can add new definitions called "version 2" and corresponding constructors for `Arguments`. One of the other remaining large questions about the fmt module were how the pending I/O reform would affect the signatures of methods in the module. Due to [RFC 526][rfc], however, the writers of fmt are now incompatible with the writers of io, so this question has largely been solved. As a result the interfaces are largely stabilized as-is today. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md Specifically, the following changes were made: * The contents of `fmt::rt` were all moved under `fmt::rt::v1` * `fmt::rt` is stable * `fmt::rt::v1` is stable * `Error` is stable * `Writer` is stable * `Writer::write_str` is stable * `Writer::write_fmt` is stable * `Formatter` is stable * `Argument` has been renamed to `ArgumentV1` and is stable * `ArgumentV1::new` is stable * `ArgumentV1::from_uint` is stable * `Arguments::new_v1` is stable (renamed from `new`) * `Arguments::new_v1_formatted` is stable (renamed from `with_placeholders`) * All formatting traits are now stable, as well as the `fmt` method. * `fmt::write` is stable * `fmt::format` is stable * `Formatter::pad_integral` is stable * `Formatter::pad` is stable * `Formatter::write_str` is stable * `Formatter::write_fmt` is stable * Some assorted top level items which were only used by `format_args!` were removed in favor of static functions on `ArgumentV1` as well. * The formatting-flag-accessing methods remain unstable Within the contents of the `fmt::rt::v1` module, the following actions were taken: * Reexports of all enum variants were removed * All prefixes on enum variants were removed * A few miscellaneous enum variants were renamed * Otherwise all structs, fields, and variants were marked stable. In addition to these actions in the `std::fmt` module, many implementations of `Show` and `String` were stabilized as well. In some other modules: * `ToString` is now stable * `ToString::to_string` is now stable * `Vec` no longer implements `fmt::Writer` (this has moved to `String`) This is a breaking change due to all of the changes to the `fmt::rt` module, but this likely will not have much impact on existing programs. Closes #20661 [breaking-change]
2015-01-13 15:42:53 -08:00
("new_v1_formatted", vec![pieces, args_slice, fmt])
};
let path = self.ecx.std_path(&["fmt", "Arguments", fn_name]);
self.ecx.expr_call_global(self.macsp, path, fn_args)
}
2016-06-06 20:22:48 +05:30
fn format_arg(ecx: &ExtCtxt,
macsp: Span,
2017-03-17 04:04:41 +00:00
mut sp: Span,
2016-06-06 20:22:48 +05:30
ty: &ArgumentType,
arg: ast::Ident)
2014-09-13 19:06:01 +03:00
-> P<ast::Expr> {
2018-03-18 23:51:53 +03:00
sp = sp.apply_mark(ecx.current_expansion.mark);
let arg = ecx.expr_ident(sp, arg);
let trait_ = match *ty {
Placeholder(ref tyname) => {
match &tyname[..] {
2016-06-06 20:22:48 +05:30
"" => "Display",
std: Rename Show/String to Debug/Display This commit is an implementation of [RFC 565][rfc] which is a stabilization of the `std::fmt` module and the implementations of various formatting traits. Specifically, the following changes were performed: [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0565-show-string-guidelines.md * The `Show` trait is now deprecated, it was renamed to `Debug` * The `String` trait is now deprecated, it was renamed to `Display` * Many `Debug` and `Display` implementations were audited in accordance with the RFC and audited implementations now have the `#[stable]` attribute * Integers and floats no longer print a suffix * Smart pointers no longer print details that they are a smart pointer * Paths with `Debug` are now quoted and escape characters * The `unwrap` methods on `Result` now require `Display` instead of `Debug` * The `Error` trait no longer has a `detail` method and now requires that `Display` must be implemented. With the loss of `String`, this has moved into libcore. * `impl<E: Error> FromError<E> for Box<Error>` now exists * `derive(Show)` has been renamed to `derive(Debug)`. This is not currently warned about due to warnings being emitted on stage1+ While backwards compatibility is attempted to be maintained with a blanket implementation of `Display` for the old `String` trait (and the same for `Show`/`Debug`) this is still a breaking change due to primitives no longer implementing `String` as well as modifications such as `unwrap` and the `Error` trait. Most code is fairly straightforward to update with a rename or tweaks of method calls. [breaking-change] Closes #21436
2015-01-20 15:45:07 -08:00
"?" => "Debug",
"e" => "LowerExp",
"E" => "UpperExp",
"o" => "Octal",
"p" => "Pointer",
"b" => "Binary",
"x" => "LowerHex",
"X" => "UpperHex",
_ => {
2016-06-06 20:22:48 +05:30
ecx.span_err(sp, &format!("unknown format trait `{}`", *tyname));
"Dummy"
}
}
}
Count => {
let path = ecx.std_path(&["fmt", "ArgumentV1", "from_usize"]);
2016-06-06 20:22:48 +05:30
return ecx.expr_call_global(macsp, path, vec![arg]);
}
};
let path = ecx.std_path(&["fmt", trait_, "fmt"]);
let format_fn = ecx.path_global(sp, path);
let path = ecx.std_path(&["fmt", "ArgumentV1", "new"]);
ecx.expr_call_global(macsp, path, vec![arg, ecx.expr_path(format_fn)])
}
}
2016-06-06 20:22:48 +05:30
pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt,
mut sp: Span,
tts: &[tokenstream::TokenTree])
2016-06-06 20:22:48 +05:30
-> Box<base::MacResult + 'cx> {
2018-03-18 23:51:53 +03:00
sp = sp.apply_mark(ecx.current_expansion.mark);
match parse_args(ecx, sp, tts) {
Some((efmt, args, names)) => {
2016-06-06 20:22:48 +05:30
MacEager::expr(expand_preparsed_format_args(ecx, sp, efmt, args, names))
}
2016-06-06 20:22:48 +05:30
None => DummyResult::expr(sp),
}
}
/// Take the various parts of `format_args!(efmt, args..., name=names...)`
/// and construct the appropriate formatting expression.
2016-06-06 20:22:48 +05:30
pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
sp: Span,
2014-09-13 19:06:01 +03:00
efmt: P<ast::Expr>,
args: Vec<P<ast::Expr>>,
names: HashMap<String, usize>)
2014-09-13 19:06:01 +03:00
-> P<ast::Expr> {
// NOTE: this verbose way of initializing `Vec<Vec<ArgumentType>>` is because
// `ArgumentType` does not derive `Clone`.
let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
let arg_unique_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
let mut macsp = ecx.call_site();
2018-03-18 23:51:53 +03:00
macsp = macsp.apply_mark(ecx.current_expansion.mark);
2016-09-02 22:01:35 +00:00
let msg = "format argument must be a string literal.";
let fmt = match expr_to_spanned_string(ecx, efmt, msg) {
Some(fmt) => fmt,
None => return DummyResult::raw_expr(sp),
};
let mut cx = Context {
ecx,
args,
arg_types,
arg_unique_types,
names,
curarg: 0,
arg_index_map: Vec::new(),
count_args: Vec::new(),
count_positions: HashMap::new(),
count_positions_count: 0,
count_args_index_offset: 0,
literal: String::new(),
pieces: Vec::new(),
2014-08-21 14:34:00 +01:00
str_pieces: Vec::new(),
all_pieces_simple: true,
macsp,
2016-09-02 22:01:35 +00:00
fmtsp: fmt.span,
invalid_refs: Vec::new(),
};
let fmt_str = &*fmt.node.0.as_str();
let mut parser = parse::Parser::new(fmt_str);
let mut pieces = vec![];
2015-02-03 23:31:06 +01:00
2018-03-05 15:58:54 -03:00
while let Some(mut piece) = parser.next() {
if !parser.errors.is_empty() {
break;
Remove std::condition This has been a long time coming. Conditions in rust were initially envisioned as being a good alternative to error code return pattern. The idea is that all errors are fatal-by-default, and you can opt-in to handling the error by registering an error handler. While sounding nice, conditions ended up having some unforseen shortcomings: * Actually handling an error has some very awkward syntax: let mut result = None; let mut answer = None; io::io_error::cond.trap(|e| { result = Some(e) }).inside(|| { answer = Some(some_io_operation()); }); match result { Some(err) => { /* hit an I/O error */ } None => { let answer = answer.unwrap(); /* deal with the result of I/O */ } } This pattern can certainly use functions like io::result, but at its core actually handling conditions is fairly difficult * The "zero value" of a function is often confusing. One of the main ideas behind using conditions was to change the signature of I/O functions. Instead of read_be_u32() returning a result, it returned a u32. Errors were notified via a condition, and if you caught the condition you understood that the "zero value" returned is actually a garbage value. These zero values are often difficult to understand, however. One case of this is the read_bytes() function. The function takes an integer length of the amount of bytes to read, and returns an array of that size. The array may actually be shorter, however, if an error occurred. Another case is fs::stat(). The theoretical "zero value" is a blank stat struct, but it's a little awkward to create and return a zero'd out stat struct on a call to stat(). In general, the return value of functions that can raise error are much more natural when using a Result as opposed to an always-usable zero-value. * Conditions impose a necessary runtime requirement on *all* I/O. In theory I/O is as simple as calling read() and write(), but using conditions imposed the restriction that a rust local task was required if you wanted to catch errors with I/O. While certainly an surmountable difficulty, this was always a bit of a thorn in the side of conditions. * Functions raising conditions are not always clear that they are raising conditions. This suffers a similar problem to exceptions where you don't actually know whether a function raises a condition or not. The documentation likely explains, but if someone retroactively adds a condition to a function there's nothing forcing upstream users to acknowledge a new point of task failure. * Libaries using I/O are not guaranteed to correctly raise on conditions when an error occurs. In developing various I/O libraries, it's much easier to just return `None` from a read rather than raising an error. The silent contract of "don't raise on EOF" was a little difficult to understand and threw a wrench into the answer of the question "when do I raise a condition?" Many of these difficulties can be overcome through documentation, examples, and general practice. In the end, all of these difficulties added together ended up being too overwhelming and improving various aspects didn't end up helping that much. A result-based I/O error handling strategy also has shortcomings, but the cognitive burden is much smaller. The tooling necessary to make this strategy as usable as conditions were is much smaller than the tooling necessary for conditions. Perhaps conditions may manifest themselves as a future entity, but for now we're going to remove them from the standard library. Closes #9795 Closes #8968
2014-02-04 19:02:10 -08:00
}
2018-03-05 15:58:54 -03:00
cx.verify_piece(&piece);
cx.resolve_name_inplace(&mut piece);
pieces.push(piece);
Remove std::condition This has been a long time coming. Conditions in rust were initially envisioned as being a good alternative to error code return pattern. The idea is that all errors are fatal-by-default, and you can opt-in to handling the error by registering an error handler. While sounding nice, conditions ended up having some unforseen shortcomings: * Actually handling an error has some very awkward syntax: let mut result = None; let mut answer = None; io::io_error::cond.trap(|e| { result = Some(e) }).inside(|| { answer = Some(some_io_operation()); }); match result { Some(err) => { /* hit an I/O error */ } None => { let answer = answer.unwrap(); /* deal with the result of I/O */ } } This pattern can certainly use functions like io::result, but at its core actually handling conditions is fairly difficult * The "zero value" of a function is often confusing. One of the main ideas behind using conditions was to change the signature of I/O functions. Instead of read_be_u32() returning a result, it returned a u32. Errors were notified via a condition, and if you caught the condition you understood that the "zero value" returned is actually a garbage value. These zero values are often difficult to understand, however. One case of this is the read_bytes() function. The function takes an integer length of the amount of bytes to read, and returns an array of that size. The array may actually be shorter, however, if an error occurred. Another case is fs::stat(). The theoretical "zero value" is a blank stat struct, but it's a little awkward to create and return a zero'd out stat struct on a call to stat(). In general, the return value of functions that can raise error are much more natural when using a Result as opposed to an always-usable zero-value. * Conditions impose a necessary runtime requirement on *all* I/O. In theory I/O is as simple as calling read() and write(), but using conditions imposed the restriction that a rust local task was required if you wanted to catch errors with I/O. While certainly an surmountable difficulty, this was always a bit of a thorn in the side of conditions. * Functions raising conditions are not always clear that they are raising conditions. This suffers a similar problem to exceptions where you don't actually know whether a function raises a condition or not. The documentation likely explains, but if someone retroactively adds a condition to a function there's nothing forcing upstream users to acknowledge a new point of task failure. * Libaries using I/O are not guaranteed to correctly raise on conditions when an error occurs. In developing various I/O libraries, it's much easier to just return `None` from a read rather than raising an error. The silent contract of "don't raise on EOF" was a little difficult to understand and threw a wrench into the answer of the question "when do I raise a condition?" Many of these difficulties can be overcome through documentation, examples, and general practice. In the end, all of these difficulties added together ended up being too overwhelming and improving various aspects didn't end up helping that much. A result-based I/O error handling strategy also has shortcomings, but the cognitive burden is much smaller. The tooling necessary to make this strategy as usable as conditions were is much smaller than the tooling necessary for conditions. Perhaps conditions may manifest themselves as a future entity, but for now we're going to remove them from the standard library. Closes #9795 Closes #8968
2014-02-04 19:02:10 -08:00
}
let numbered_position_args = pieces.iter().any(|arg: &parse::Piece| {
match *arg {
parse::String(_) => false,
parse::NextArgument(arg) => {
match arg.position {
parse::Position::ArgumentIs(_) => true,
_ => false,
}
}
}
});
cx.build_index_map();
let mut arg_index_consumed = vec![0usize; cx.arg_index_map.len()];
for piece in pieces {
2018-05-08 16:10:16 +03:00
if let Some(piece) = cx.build_piece(&piece, &mut arg_index_consumed) {
let s = cx.build_literal_string();
cx.str_pieces.push(s);
cx.pieces.push(piece);
}
}
2014-12-30 10:51:18 -08:00
if !parser.errors.is_empty() {
let err = parser.errors.remove(0);
let sp = cx.fmtsp.from_inner_byte_pos(err.start, err.end);
let mut e = cx.ecx.struct_span_err(sp, &format!("invalid format string: {}",
err.description));
e.span_label(sp, err.label + " in format string");
if let Some(note) = err.note {
e.note(&note);
}
e.emit();
2014-12-30 10:51:18 -08:00
return DummyResult::raw_expr(sp);
Remove std::condition This has been a long time coming. Conditions in rust were initially envisioned as being a good alternative to error code return pattern. The idea is that all errors are fatal-by-default, and you can opt-in to handling the error by registering an error handler. While sounding nice, conditions ended up having some unforseen shortcomings: * Actually handling an error has some very awkward syntax: let mut result = None; let mut answer = None; io::io_error::cond.trap(|e| { result = Some(e) }).inside(|| { answer = Some(some_io_operation()); }); match result { Some(err) => { /* hit an I/O error */ } None => { let answer = answer.unwrap(); /* deal with the result of I/O */ } } This pattern can certainly use functions like io::result, but at its core actually handling conditions is fairly difficult * The "zero value" of a function is often confusing. One of the main ideas behind using conditions was to change the signature of I/O functions. Instead of read_be_u32() returning a result, it returned a u32. Errors were notified via a condition, and if you caught the condition you understood that the "zero value" returned is actually a garbage value. These zero values are often difficult to understand, however. One case of this is the read_bytes() function. The function takes an integer length of the amount of bytes to read, and returns an array of that size. The array may actually be shorter, however, if an error occurred. Another case is fs::stat(). The theoretical "zero value" is a blank stat struct, but it's a little awkward to create and return a zero'd out stat struct on a call to stat(). In general, the return value of functions that can raise error are much more natural when using a Result as opposed to an always-usable zero-value. * Conditions impose a necessary runtime requirement on *all* I/O. In theory I/O is as simple as calling read() and write(), but using conditions imposed the restriction that a rust local task was required if you wanted to catch errors with I/O. While certainly an surmountable difficulty, this was always a bit of a thorn in the side of conditions. * Functions raising conditions are not always clear that they are raising conditions. This suffers a similar problem to exceptions where you don't actually know whether a function raises a condition or not. The documentation likely explains, but if someone retroactively adds a condition to a function there's nothing forcing upstream users to acknowledge a new point of task failure. * Libaries using I/O are not guaranteed to correctly raise on conditions when an error occurs. In developing various I/O libraries, it's much easier to just return `None` from a read rather than raising an error. The silent contract of "don't raise on EOF" was a little difficult to understand and threw a wrench into the answer of the question "when do I raise a condition?" Many of these difficulties can be overcome through documentation, examples, and general practice. In the end, all of these difficulties added together ended up being too overwhelming and improving various aspects didn't end up helping that much. A result-based I/O error handling strategy also has shortcomings, but the cognitive burden is much smaller. The tooling necessary to make this strategy as usable as conditions were is much smaller than the tooling necessary for conditions. Perhaps conditions may manifest themselves as a future entity, but for now we're going to remove them from the standard library. Closes #9795 Closes #8968
2014-02-04 19:02:10 -08:00
}
2014-08-21 14:34:00 +01:00
if !cx.literal.is_empty() {
2018-05-08 16:10:16 +03:00
let s = cx.build_literal_string();
2014-08-21 14:34:00 +01:00
cx.str_pieces.push(s);
}
if cx.invalid_refs.len() >= 1 {
cx.report_invalid_references(numbered_position_args);
}
// Make sure that all arguments were used and all arguments have types.
let num_pos_args = cx.args.len() - cx.names.len();
let mut errs = vec![];
for (i, ty) in cx.arg_types.iter().enumerate() {
if ty.len() == 0 {
if cx.count_positions.contains_key(&i) {
continue;
}
let msg = if i >= num_pos_args {
// named argument
"named argument never used"
} else {
// positional argument
"argument never used"
};
errs.push((cx.args[i].span, msg));
}
}
if errs.len() > 0 {
let args_used = cx.arg_types.len() - errs.len();
let args_unused = errs.len();
let mut diag = {
if errs.len() == 1 {
let (sp, msg) = errs.into_iter().next().unwrap();
cx.ecx.struct_span_err(sp, msg)
} else {
let mut diag = cx.ecx.struct_span_err(
errs.iter().map(|&(sp, _)| sp).collect::<Vec<Span>>(),
"multiple unused formatting arguments"
);
diag.span_label(cx.fmtsp, "multiple unused arguments in this statement");
diag
}
};
// Decide if we want to look for foreign formatting directives.
if args_used < args_unused {
use super::format_foreign as foreign;
// The set of foreign substitutions we've explained. This prevents spamming the user
// with `%d should be written as {}` over and over again.
let mut explained = HashSet::new();
// Used to ensure we only report translations for *one* kind of foreign format.
let mut found_foreign = false;
macro_rules! check_foreign {
($kind:ident) => {{
let mut show_doc_note = false;
for sub in foreign::$kind::iter_subs(fmt_str) {
let trn = match sub.translate() {
Some(trn) => trn,
// If it has no translation, don't call it out specifically.
None => continue,
};
let sub = String::from(sub.as_str());
if explained.contains(&sub) {
continue;
}
explained.insert(sub.clone());
if !found_foreign {
found_foreign = true;
show_doc_note = true;
}
diag.help(&format!("`{}` should be written as `{}`", sub, trn));
}
if show_doc_note {
diag.note(concat!(stringify!($kind), " formatting not supported; see \
the documentation for `std::fmt`"));
}
}};
}
check_foreign!(printf);
if !found_foreign {
check_foreign!(shell);
}
}
diag.emit();
}
cx.into_expr()
}