std: Enforce Unicode in fmt::Writer
This commit is an implementation of [RFC 526][rfc] which is a change to alter the definition of the old `fmt::FormatWriter`. The new trait, renamed to `Writer`, now only exposes one method `write_str` in order to guarantee that all implementations of the formatting traits can only produce valid Unicode. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md One of the primary improvements of this patch is the performance of the `.to_string()` method by avoiding an almost-always redundant UTF-8 check. This is a breaking change due to the renaming of the trait as well as the loss of the `write` method, but migration paths should be relatively easy: * All usage of `write` should move to `write_str`. If truly binary data was being written in an implementation of `Show`, then it will need to use a different trait or an altogether different code path. * All usage of `write!` should continue to work as-is with no modifications. * All usage of `Show` where implementations just delegate to another should continue to work as-is. [breaking-change] Closes #20352
This commit is contained in:
parent
cd614164e6
commit
e423fcf0e0
25 changed files with 320 additions and 359 deletions
|
|
@ -29,7 +29,7 @@ impl<'a> fmt::Show for Escape<'a> {
|
|||
for (i, ch) in s.bytes().enumerate() {
|
||||
match ch as char {
|
||||
'<' | '>' | '&' | '\'' | '"' => {
|
||||
try!(fmt.write(pile_o_bits.slice(last, i).as_bytes()));
|
||||
try!(fmt.write_str(pile_o_bits.slice(last, i)));
|
||||
let s = match ch as char {
|
||||
'>' => ">",
|
||||
'<' => "<",
|
||||
|
|
@ -38,7 +38,7 @@ impl<'a> fmt::Show for Escape<'a> {
|
|||
'"' => """,
|
||||
_ => unreachable!()
|
||||
};
|
||||
try!(fmt.write(s.as_bytes()));
|
||||
try!(fmt.write_str(s));
|
||||
last = i + 1;
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -46,7 +46,7 @@ impl<'a> fmt::Show for Escape<'a> {
|
|||
}
|
||||
|
||||
if last < s.len() {
|
||||
try!(fmt.write(pile_o_bits.slice_from(last).as_bytes()));
|
||||
try!(fmt.write_str(pile_o_bits.slice_from(last)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ impl<'a> fmt::Show for TyParamBounds<'a> {
|
|||
let &TyParamBounds(bounds) = self;
|
||||
for (i, bound) in bounds.iter().enumerate() {
|
||||
if i > 0 {
|
||||
try!(f.write(" + ".as_bytes()));
|
||||
try!(f.write_str(" + "));
|
||||
}
|
||||
try!(write!(f, "{}", *bound));
|
||||
}
|
||||
|
|
@ -80,24 +80,24 @@ impl<'a> fmt::Show for TyParamBounds<'a> {
|
|||
impl fmt::Show for clean::Generics {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) }
|
||||
try!(f.write("<".as_bytes()));
|
||||
try!(f.write_str("<"));
|
||||
|
||||
for (i, life) in self.lifetimes.iter().enumerate() {
|
||||
if i > 0 {
|
||||
try!(f.write(", ".as_bytes()));
|
||||
try!(f.write_str(", "));
|
||||
}
|
||||
try!(write!(f, "{}", *life));
|
||||
}
|
||||
|
||||
if self.type_params.len() > 0 {
|
||||
if self.lifetimes.len() > 0 {
|
||||
try!(f.write(", ".as_bytes()));
|
||||
try!(f.write_str(", "));
|
||||
}
|
||||
for (i, tp) in self.type_params.iter().enumerate() {
|
||||
if i > 0 {
|
||||
try!(f.write(", ".as_bytes()))
|
||||
try!(f.write_str(", "))
|
||||
}
|
||||
try!(f.write(tp.name.as_bytes()));
|
||||
try!(f.write_str(tp.name[]));
|
||||
|
||||
if tp.bounds.len() > 0 {
|
||||
try!(write!(f, ": {}", TyParamBounds(tp.bounds.as_slice())));
|
||||
|
|
@ -109,7 +109,7 @@ impl fmt::Show for clean::Generics {
|
|||
};
|
||||
}
|
||||
}
|
||||
try!(f.write(">".as_bytes()));
|
||||
try!(f.write_str(">"));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -120,10 +120,10 @@ impl<'a> fmt::Show for WhereClause<'a> {
|
|||
if gens.where_predicates.len() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
try!(f.write(" <span class='where'>where ".as_bytes()));
|
||||
try!(f.write_str(" <span class='where'>where "));
|
||||
for (i, pred) in gens.where_predicates.iter().enumerate() {
|
||||
if i > 0 {
|
||||
try!(f.write(", ".as_bytes()));
|
||||
try!(f.write_str(", "));
|
||||
}
|
||||
match pred {
|
||||
&clean::WherePredicate::BoundPredicate { ref ty, ref bounds } => {
|
||||
|
|
@ -135,7 +135,7 @@ impl<'a> fmt::Show for WhereClause<'a> {
|
|||
try!(write!(f, "{}: ", lifetime));
|
||||
for (i, lifetime) in bounds.iter().enumerate() {
|
||||
if i > 0 {
|
||||
try!(f.write(" + ".as_bytes()));
|
||||
try!(f.write_str(" + "));
|
||||
}
|
||||
|
||||
try!(write!(f, "{}", lifetime));
|
||||
|
|
@ -146,14 +146,14 @@ impl<'a> fmt::Show for WhereClause<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
try!(f.write("</span>".as_bytes()));
|
||||
try!(f.write_str("</span>"));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for clean::Lifetime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(f.write(self.get_ref().as_bytes()));
|
||||
try!(f.write_str(self.get_ref()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -161,14 +161,14 @@ impl fmt::Show for clean::Lifetime {
|
|||
impl fmt::Show for clean::PolyTrait {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.lifetimes.len() > 0 {
|
||||
try!(f.write("for<".as_bytes()));
|
||||
try!(f.write_str("for<"));
|
||||
for (i, lt) in self.lifetimes.iter().enumerate() {
|
||||
if i > 0 {
|
||||
try!(f.write(", ".as_bytes()));
|
||||
try!(f.write_str(", "));
|
||||
}
|
||||
try!(write!(f, "{}", lt));
|
||||
}
|
||||
try!(f.write("> ".as_bytes()));
|
||||
try!(f.write_str("> "));
|
||||
}
|
||||
write!(f, "{}", self.trait_)
|
||||
}
|
||||
|
|
@ -196,38 +196,38 @@ impl fmt::Show for clean::PathParameters {
|
|||
match *self {
|
||||
clean::PathParameters::AngleBracketed { ref lifetimes, ref types } => {
|
||||
if lifetimes.len() > 0 || types.len() > 0 {
|
||||
try!(f.write("<".as_bytes()));
|
||||
try!(f.write_str("<"));
|
||||
let mut comma = false;
|
||||
for lifetime in lifetimes.iter() {
|
||||
if comma {
|
||||
try!(f.write(", ".as_bytes()));
|
||||
try!(f.write_str(", "));
|
||||
}
|
||||
comma = true;
|
||||
try!(write!(f, "{}", *lifetime));
|
||||
}
|
||||
for ty in types.iter() {
|
||||
if comma {
|
||||
try!(f.write(", ".as_bytes()));
|
||||
try!(f.write_str(", "));
|
||||
}
|
||||
comma = true;
|
||||
try!(write!(f, "{}", *ty));
|
||||
}
|
||||
try!(f.write(">".as_bytes()));
|
||||
try!(f.write_str(">"));
|
||||
}
|
||||
}
|
||||
clean::PathParameters::Parenthesized { ref inputs, ref output } => {
|
||||
try!(f.write("(".as_bytes()));
|
||||
try!(f.write_str("("));
|
||||
let mut comma = false;
|
||||
for ty in inputs.iter() {
|
||||
if comma {
|
||||
try!(f.write(", ".as_bytes()));
|
||||
try!(f.write_str(", "));
|
||||
}
|
||||
comma = true;
|
||||
try!(write!(f, "{}", *ty));
|
||||
}
|
||||
try!(f.write(")".as_bytes()));
|
||||
try!(f.write_str(")"));
|
||||
if let Some(ref ty) = *output {
|
||||
try!(f.write(" -> ".as_bytes()));
|
||||
try!(f.write_str(" -> "));
|
||||
try!(write!(f, "{}", ty));
|
||||
}
|
||||
}
|
||||
|
|
@ -238,7 +238,7 @@ impl fmt::Show for clean::PathParameters {
|
|||
|
||||
impl fmt::Show for clean::PathSegment {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(f.write(self.name.as_bytes()));
|
||||
try!(f.write_str(self.name.as_slice()));
|
||||
write!(f, "{}", self.params)
|
||||
}
|
||||
}
|
||||
|
|
@ -246,12 +246,12 @@ impl fmt::Show for clean::PathSegment {
|
|||
impl fmt::Show for clean::Path {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.global {
|
||||
try!(f.write("::".as_bytes()))
|
||||
try!(f.write_str("::"))
|
||||
}
|
||||
|
||||
for (i, seg) in self.segments.iter().enumerate() {
|
||||
if i > 0 {
|
||||
try!(f.write("::".as_bytes()))
|
||||
try!(f.write_str("::"))
|
||||
}
|
||||
try!(write!(f, "{}", seg));
|
||||
}
|
||||
|
|
@ -433,10 +433,10 @@ impl fmt::Show for clean::Type {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
clean::TyParamBinder(id) => {
|
||||
f.write(cache().typarams[ast_util::local_def(id)].as_bytes())
|
||||
f.write_str(cache().typarams[ast_util::local_def(id)][])
|
||||
}
|
||||
clean::Generic(ref name) => {
|
||||
f.write(name.as_bytes())
|
||||
f.write_str(name.as_slice())
|
||||
}
|
||||
clean::ResolvedPath{ did, ref typarams, ref path } => {
|
||||
try!(resolved_path(f, did, path, false));
|
||||
|
|
@ -522,7 +522,7 @@ impl fmt::Show for clean::Type {
|
|||
primitive_link(f, clean::Slice,
|
||||
format!("[{}, ..{}]", **t, *s).as_slice())
|
||||
}
|
||||
clean::Bottom => f.write("!".as_bytes()),
|
||||
clean::Bottom => f.write_str("!"),
|
||||
clean::RawPointer(m, ref t) => {
|
||||
write!(f, "*{}{}", RawMutableSpace(m), **t)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
|
|||
|
||||
if ret.is_ok() {
|
||||
let buf = slice::from_raw_buf(&(*ob).data, (*ob).size as uint);
|
||||
ret = w.write(buf);
|
||||
ret = w.write_str(str::from_utf8(buf).unwrap());
|
||||
}
|
||||
hoedown_buffer_free(ob);
|
||||
ret
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ use std::sync::Arc;
|
|||
use externalfiles::ExternalHtml;
|
||||
|
||||
use serialize::json;
|
||||
use serialize::Encodable;
|
||||
use serialize::json::ToJson;
|
||||
use syntax::ast;
|
||||
use syntax::ast_util;
|
||||
|
|
@ -1095,7 +1094,7 @@ impl Context {
|
|||
try!(self.recurse(stability.name.clone(), |this| {
|
||||
let json_dst = &this.dst.join("stability.json");
|
||||
let mut json_out = BufferedWriter::new(try!(File::create(json_dst)));
|
||||
try!(stability.encode(&mut json::Encoder::new(&mut json_out)));
|
||||
try!(write!(&mut json_out, "{}", json::as_json(&stability)));
|
||||
|
||||
let mut title = stability.name.clone();
|
||||
title.push_str(" - Stability dashboard");
|
||||
|
|
@ -1311,7 +1310,8 @@ impl<'a> Item<'a> {
|
|||
// has anchors for the line numbers that we're linking to.
|
||||
if ast_util::is_local(self.item.def_id) {
|
||||
let mut path = Vec::new();
|
||||
clean_srcpath(&cx.src_root, self.item.source.filename.as_bytes(), |component| {
|
||||
clean_srcpath(&cx.src_root, self.item.source.filename.as_bytes(),
|
||||
|component| {
|
||||
path.push(component.to_string());
|
||||
});
|
||||
let href = if self.item.source.loline == self.item.source.hiline {
|
||||
|
|
@ -1713,7 +1713,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
|
|||
try!(write!(w, ";\n"));
|
||||
}
|
||||
if types.len() > 0 && required.len() > 0 {
|
||||
try!(w.write("\n".as_bytes()));
|
||||
try!(w.write_str("\n"));
|
||||
}
|
||||
for m in required.iter() {
|
||||
try!(write!(w, " "));
|
||||
|
|
@ -1721,7 +1721,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
|
|||
try!(write!(w, ";\n"));
|
||||
}
|
||||
if required.len() > 0 && provided.len() > 0 {
|
||||
try!(w.write("\n".as_bytes()));
|
||||
try!(w.write_str("\n"));
|
||||
}
|
||||
for m in provided.iter() {
|
||||
try!(write!(w, " "));
|
||||
|
|
@ -2260,8 +2260,9 @@ impl<'a> fmt::Show for Source<'a> {
|
|||
|
||||
fn item_macro(w: &mut fmt::Formatter, it: &clean::Item,
|
||||
t: &clean::Macro) -> fmt::Result {
|
||||
try!(w.write(highlight::highlight(t.source.as_slice(), Some("macro"),
|
||||
None).as_bytes()));
|
||||
try!(w.write_str(highlight::highlight(t.source.as_slice(),
|
||||
Some("macro"),
|
||||
None)[]));
|
||||
document(w, it)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue