Optimize the code produced by derive(Debug)
.
This commit adds new methods that combine sequences of existing formatting methods. - `Formatter::debug_{tuple,struct}_field[12345]_finish`, equivalent to a `Formatter::debug_{tuple,struct}` + N x `Debug{Tuple,Struct}::field` + `Debug{Tuple,Struct}::finish` call sequence. - `Formatter::debug_{tuple,struct}_fields_finish` is similar, but can handle any number of fields by using arrays. These new methods are all marked as `doc(hidden)` and unstable. They are intended for the compiler's own use. Special-casing up to 5 fields gives significantly better performance results than always using arrays (as was tried in #95637). The commit also changes the `Debug` deriving code to use these new methods. For example, where the old `Debug` code for a struct with two fields would be like this: ``` fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { match *self { Self { f1: ref __self_0_0, f2: ref __self_0_1, } => { let debug_trait_builder = &mut ::core::fmt::Formatter::debug_struct(f, "S2"); let _ = ::core::fmt::DebugStruct::field(debug_trait_builder, "f1", &&(*__self_0_0)); let _ = ::core::fmt::DebugStruct::field(debug_trait_builder, "f2", &&(*__self_0_1)); ::core::fmt::DebugStruct::finish(debug_trait_builder) } } } ``` the new code is like this: ``` fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { match *self { Self { f1: ref __self_0_0, f2: ref __self_0_1, } => ::core::fmt::Formatter::debug_struct_field2_finish( f, "S2", "f1", &&(*__self_0_0), "f2", &&(*__self_0_1), ), } } ``` This shrinks the code produced for `Debug` instances considerably, reducing compile times and binary sizes. Co-authored-by: Scott McMurray <scottmcm@users.noreply.github.com>
This commit is contained in:
parent
7586e79af8
commit
5b54363961
@ -3,14 +3,10 @@ use crate::deriving::generic::*;
|
|||||||
use crate::deriving::path_std;
|
use crate::deriving::path_std;
|
||||||
|
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::{self as ast, Expr, LocalKind, MetaItem};
|
use rustc_ast::{self as ast, Expr, MetaItem};
|
||||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||||
use rustc_span::symbol::{sym, Ident};
|
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||||
use rustc_span::{Span, DUMMY_SP};
|
use rustc_span::Span;
|
||||||
|
|
||||||
fn make_mut_borrow(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<Expr>) -> P<Expr> {
|
|
||||||
cx.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Mut, expr))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expand_deriving_debug(
|
pub fn expand_deriving_debug(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
@ -49,11 +45,7 @@ pub fn expand_deriving_debug(
|
|||||||
trait_def.expand(cx, mitem, item, push)
|
trait_def.expand(cx, mitem, item, push)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We use the debug builders to do the heavy lifting here
|
|
||||||
fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
|
fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
|
||||||
// build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build()
|
|
||||||
// or fmt.debug_tuple(<name>).field(&<fieldval>)....build()
|
|
||||||
// based on the "shape".
|
|
||||||
let (ident, vdata, fields) = match substr.fields {
|
let (ident, vdata, fields) = match substr.fields {
|
||||||
Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
|
Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
|
||||||
EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
|
EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
|
||||||
@ -67,93 +59,130 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
|
|||||||
let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked));
|
let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked));
|
||||||
let fmt = substr.nonself_args[0].clone();
|
let fmt = substr.nonself_args[0].clone();
|
||||||
|
|
||||||
// Special fast path for unit variants. In the common case of an enum that is entirely unit
|
// Struct and tuples are similar enough that we use the same code for both,
|
||||||
// variants (i.e. a C-like enum), this fast path allows LLVM to eliminate the entire switch in
|
// with some extra pieces for structs due to the field names.
|
||||||
// favor of a lookup table.
|
let (is_struct, args_per_field) = match vdata {
|
||||||
if let ast::VariantData::Unit(..) = vdata {
|
|
||||||
let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
|
|
||||||
let expr = cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]);
|
|
||||||
let stmts = vec![cx.stmt_expr(expr)];
|
|
||||||
let block = cx.block(span, stmts);
|
|
||||||
return cx.expr_block(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
let builder = Ident::new(sym::debug_trait_builder, span);
|
|
||||||
let builder_expr = cx.expr_ident(span, builder);
|
|
||||||
|
|
||||||
let mut stmts = Vec::with_capacity(fields.len() + 2);
|
|
||||||
let fn_path_finish;
|
|
||||||
match vdata {
|
|
||||||
ast::VariantData::Unit(..) => {
|
ast::VariantData::Unit(..) => {
|
||||||
cx.span_bug(span, "unit variants should have been handled above");
|
// Special fast path for unit variants.
|
||||||
|
//let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
|
||||||
|
//return cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]);
|
||||||
|
assert!(fields.is_empty());
|
||||||
|
(false, 0)
|
||||||
}
|
}
|
||||||
ast::VariantData::Tuple(..) => {
|
ast::VariantData::Tuple(..) => (false, 1),
|
||||||
// tuple struct/"normal" variant
|
ast::VariantData::Struct(..) => (true, 2),
|
||||||
let fn_path_debug_tuple = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_tuple]);
|
};
|
||||||
let expr = cx.expr_call_global(span, fn_path_debug_tuple, vec![fmt, name]);
|
|
||||||
let expr = make_mut_borrow(cx, span, expr);
|
|
||||||
stmts.push(cx.stmt_let(span, false, builder, expr));
|
|
||||||
|
|
||||||
for field in fields {
|
// The number of fields that can be handled without an array.
|
||||||
// Use double indirection to make sure this works for unsized types
|
const CUTOFF: usize = 5;
|
||||||
let field = cx.expr_addr_of(field.span, field.self_.clone());
|
|
||||||
let field = cx.expr_addr_of(field.span, field);
|
|
||||||
|
|
||||||
let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::field]);
|
if fields.is_empty() {
|
||||||
let expr =
|
// Special case for no fields.
|
||||||
cx.expr_call_global(span, fn_path_field, vec![builder_expr.clone(), field]);
|
let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
|
||||||
|
cx.expr_call_global(span, fn_path_write_str, vec![fmt, name])
|
||||||
|
} else if fields.len() <= CUTOFF {
|
||||||
|
// Few enough fields that we can use a specific-length method.
|
||||||
|
let debug = if is_struct {
|
||||||
|
format!("debug_struct_field{}_finish", fields.len())
|
||||||
|
} else {
|
||||||
|
format!("debug_tuple_field{}_finish", fields.len())
|
||||||
|
};
|
||||||
|
let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]);
|
||||||
|
|
||||||
// Use `let _ = expr;` to avoid triggering the
|
let mut args = Vec::with_capacity(2 + fields.len() * args_per_field);
|
||||||
// unused_results lint.
|
args.extend([fmt, name]);
|
||||||
stmts.push(stmt_let_underscore(cx, span, expr));
|
for i in 0..fields.len() {
|
||||||
}
|
let field = &fields[i];
|
||||||
|
if is_struct {
|
||||||
fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::finish]);
|
|
||||||
}
|
|
||||||
ast::VariantData::Struct(..) => {
|
|
||||||
// normal struct/struct variant
|
|
||||||
let fn_path_debug_struct = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_struct]);
|
|
||||||
let expr = cx.expr_call_global(span, fn_path_debug_struct, vec![fmt, name]);
|
|
||||||
let expr = make_mut_borrow(cx, span, expr);
|
|
||||||
stmts.push(cx.stmt_let(DUMMY_SP, false, builder, expr));
|
|
||||||
|
|
||||||
for field in fields {
|
|
||||||
let name = cx.expr_lit(
|
let name = cx.expr_lit(
|
||||||
field.span,
|
field.span,
|
||||||
ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked),
|
ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked),
|
||||||
);
|
);
|
||||||
|
args.push(name);
|
||||||
// Use double indirection to make sure this works for unsized types
|
|
||||||
let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::field]);
|
|
||||||
let field = cx.expr_addr_of(field.span, field.self_.clone());
|
|
||||||
let field = cx.expr_addr_of(field.span, field);
|
|
||||||
let expr = cx.expr_call_global(
|
|
||||||
span,
|
|
||||||
fn_path_field,
|
|
||||||
vec![builder_expr.clone(), name, field],
|
|
||||||
);
|
|
||||||
stmts.push(stmt_let_underscore(cx, span, expr));
|
|
||||||
}
|
}
|
||||||
fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::finish]);
|
// Use double indirection to make sure this works for unsized types
|
||||||
|
let field = cx.expr_addr_of(field.span, field.self_.clone());
|
||||||
|
let field = cx.expr_addr_of(field.span, field);
|
||||||
|
args.push(field);
|
||||||
}
|
}
|
||||||
|
cx.expr_call_global(span, fn_path_debug, args)
|
||||||
|
} else {
|
||||||
|
// Enough fields that we must use the any-length method.
|
||||||
|
let mut name_exprs = Vec::with_capacity(fields.len());
|
||||||
|
let mut value_exprs = Vec::with_capacity(fields.len());
|
||||||
|
|
||||||
|
for field in fields {
|
||||||
|
if is_struct {
|
||||||
|
name_exprs.push(cx.expr_lit(
|
||||||
|
field.span,
|
||||||
|
ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use double indirection to make sure this works for unsized types
|
||||||
|
let value_ref = cx.expr_addr_of(field.span, field.self_.clone());
|
||||||
|
value_exprs.push(cx.expr_addr_of(field.span, value_ref));
|
||||||
|
}
|
||||||
|
|
||||||
|
// `let names: &'static _ = &["field1", "field2"];`
|
||||||
|
let names_let = if is_struct {
|
||||||
|
let lt_static = Some(cx.lifetime_static(span));
|
||||||
|
let ty_static_ref =
|
||||||
|
cx.ty_rptr(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
|
||||||
|
Some(cx.stmt_let_ty(
|
||||||
|
span,
|
||||||
|
false,
|
||||||
|
Ident::new(sym::names, span),
|
||||||
|
Some(ty_static_ref),
|
||||||
|
cx.expr_array_ref(span, name_exprs),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// `let values: &[&dyn Debug] = &[&&self.field1, &&self.field2];`
|
||||||
|
let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug]));
|
||||||
|
let ty_dyn_debug = cx.ty(
|
||||||
|
span,
|
||||||
|
ast::TyKind::TraitObject(vec![cx.trait_bound(path_debug)], ast::TraitObjectSyntax::Dyn),
|
||||||
|
);
|
||||||
|
let ty_slice = cx.ty(
|
||||||
|
span,
|
||||||
|
ast::TyKind::Slice(cx.ty_rptr(span, ty_dyn_debug, None, ast::Mutability::Not)),
|
||||||
|
);
|
||||||
|
let values_let = cx.stmt_let_ty(
|
||||||
|
span,
|
||||||
|
false,
|
||||||
|
Ident::new(sym::values, span),
|
||||||
|
Some(cx.ty_rptr(span, ty_slice, None, ast::Mutability::Not)),
|
||||||
|
cx.expr_array_ref(span, value_exprs),
|
||||||
|
);
|
||||||
|
|
||||||
|
// `fmt::Formatter::debug_struct_fields_finish(fmt, name, names, values)` or
|
||||||
|
// `fmt::Formatter::debug_tuple_fields_finish(fmt, name, values)`
|
||||||
|
let sym_debug = if is_struct {
|
||||||
|
sym::debug_struct_fields_finish
|
||||||
|
} else {
|
||||||
|
sym::debug_tuple_fields_finish
|
||||||
|
};
|
||||||
|
let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]);
|
||||||
|
|
||||||
|
let mut args = Vec::with_capacity(4);
|
||||||
|
args.push(fmt);
|
||||||
|
args.push(name);
|
||||||
|
if is_struct {
|
||||||
|
args.push(cx.expr_ident(span, Ident::new(sym::names, span)));
|
||||||
|
}
|
||||||
|
args.push(cx.expr_ident(span, Ident::new(sym::values, span)));
|
||||||
|
let expr = cx.expr_call_global(span, fn_path_debug_internal, args);
|
||||||
|
|
||||||
|
let mut stmts = Vec::with_capacity(3);
|
||||||
|
if is_struct {
|
||||||
|
stmts.push(names_let.unwrap());
|
||||||
|
}
|
||||||
|
stmts.push(values_let);
|
||||||
|
stmts.push(cx.stmt_expr(expr));
|
||||||
|
|
||||||
|
cx.expr_block(cx.block(span, stmts))
|
||||||
}
|
}
|
||||||
|
|
||||||
let expr = cx.expr_call_global(span, fn_path_finish, vec![builder_expr]);
|
|
||||||
|
|
||||||
stmts.push(cx.stmt_expr(expr));
|
|
||||||
let block = cx.block(span, stmts);
|
|
||||||
cx.expr_block(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> ast::Stmt {
|
|
||||||
let local = P(ast::Local {
|
|
||||||
pat: cx.pat_wild(sp),
|
|
||||||
ty: None,
|
|
||||||
id: ast::DUMMY_NODE_ID,
|
|
||||||
kind: LocalKind::Init(expr),
|
|
||||||
span: sp,
|
|
||||||
attrs: ast::AttrVec::new(),
|
|
||||||
tokens: None,
|
|
||||||
});
|
|
||||||
ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp }
|
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,10 @@ impl<'a> ExtCtxt<'a> {
|
|||||||
P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind, tokens: None })
|
P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind, tokens: None })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ty_infer(&self, span: Span) -> P<ast::Ty> {
|
||||||
|
self.ty(span, ast::TyKind::Infer)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ty_path(&self, path: ast::Path) -> P<ast::Ty> {
|
pub fn ty_path(&self, path: ast::Path) -> P<ast::Ty> {
|
||||||
self.ty(path.span, ast::TyKind::Path(None, path))
|
self.ty(path.span, ast::TyKind::Path(None, path))
|
||||||
}
|
}
|
||||||
@ -140,11 +144,26 @@ impl<'a> ExtCtxt<'a> {
|
|||||||
ast::Lifetime { id: ast::DUMMY_NODE_ID, ident: ident.with_span_pos(span) }
|
ast::Lifetime { id: ast::DUMMY_NODE_ID, ident: ident.with_span_pos(span) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lifetime_static(&self, span: Span) -> ast::Lifetime {
|
||||||
|
self.lifetime(span, Ident::new(kw::StaticLifetime, span))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt {
|
pub fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt {
|
||||||
ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) }
|
ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt {
|
pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt {
|
||||||
|
self.stmt_let_ty(sp, mutbl, ident, None, ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stmt_let_ty(
|
||||||
|
&self,
|
||||||
|
sp: Span,
|
||||||
|
mutbl: bool,
|
||||||
|
ident: Ident,
|
||||||
|
ty: Option<P<ast::Ty>>,
|
||||||
|
ex: P<ast::Expr>,
|
||||||
|
) -> ast::Stmt {
|
||||||
let pat = if mutbl {
|
let pat = if mutbl {
|
||||||
let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut);
|
let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut);
|
||||||
self.pat_ident_binding_mode(sp, ident, binding_mode)
|
self.pat_ident_binding_mode(sp, ident, binding_mode)
|
||||||
@ -153,7 +172,7 @@ impl<'a> ExtCtxt<'a> {
|
|||||||
};
|
};
|
||||||
let local = P(ast::Local {
|
let local = P(ast::Local {
|
||||||
pat,
|
pat,
|
||||||
ty: None,
|
ty,
|
||||||
id: ast::DUMMY_NODE_ID,
|
id: ast::DUMMY_NODE_ID,
|
||||||
kind: LocalKind::Init(ex),
|
kind: LocalKind::Init(ex),
|
||||||
span: sp,
|
span: sp,
|
||||||
|
@ -567,8 +567,10 @@ symbols! {
|
|||||||
debug_assert_ne_macro,
|
debug_assert_ne_macro,
|
||||||
debug_assertions,
|
debug_assertions,
|
||||||
debug_struct,
|
debug_struct,
|
||||||
|
debug_struct_fields_finish,
|
||||||
debug_trait_builder,
|
debug_trait_builder,
|
||||||
debug_tuple,
|
debug_tuple,
|
||||||
|
debug_tuple_fields_finish,
|
||||||
debugger_visualizer,
|
debugger_visualizer,
|
||||||
decl_macro,
|
decl_macro,
|
||||||
declare_lint_pass,
|
declare_lint_pass,
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use crate::cell::{Cell, Ref, RefCell, RefMut, SyncUnsafeCell, UnsafeCell};
|
use crate::cell::{Cell, Ref, RefCell, RefMut, SyncUnsafeCell, UnsafeCell};
|
||||||
use crate::char::EscapeDebugExtArgs;
|
use crate::char::EscapeDebugExtArgs;
|
||||||
|
use crate::iter;
|
||||||
use crate::marker::PhantomData;
|
use crate::marker::PhantomData;
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
use crate::num::fmt as numfmt;
|
use crate::num::fmt as numfmt;
|
||||||
@ -693,7 +694,7 @@ pub(crate) mod macros {
|
|||||||
/// Derive macro generating an impl of the trait `Debug`.
|
/// Derive macro generating an impl of the trait `Debug`.
|
||||||
#[rustc_builtin_macro]
|
#[rustc_builtin_macro]
|
||||||
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||||
#[allow_internal_unstable(core_intrinsics)]
|
#[allow_internal_unstable(core_intrinsics, fmt_helpers_for_derive)]
|
||||||
pub macro Debug($item:item) {
|
pub macro Debug($item:item) {
|
||||||
/* compiler built-in */
|
/* compiler built-in */
|
||||||
}
|
}
|
||||||
@ -1964,6 +1965,129 @@ impl<'a> Formatter<'a> {
|
|||||||
builders::debug_struct_new(self, name)
|
builders::debug_struct_new(self, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries.
|
||||||
|
/// `debug_struct_fields_finish` is more general, but this is faster for 1 field.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
|
||||||
|
pub fn debug_struct_field1_finish<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
name: &str,
|
||||||
|
name1: &str,
|
||||||
|
value1: &dyn Debug,
|
||||||
|
) -> Result {
|
||||||
|
let mut builder = builders::debug_struct_new(self, name);
|
||||||
|
builder.field(name1, value1);
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries.
|
||||||
|
/// `debug_struct_fields_finish` is more general, but this is faster for 2 fields.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
|
||||||
|
pub fn debug_struct_field2_finish<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
name: &str,
|
||||||
|
name1: &str,
|
||||||
|
value1: &dyn Debug,
|
||||||
|
name2: &str,
|
||||||
|
value2: &dyn Debug,
|
||||||
|
) -> Result {
|
||||||
|
let mut builder = builders::debug_struct_new(self, name);
|
||||||
|
builder.field(name1, value1);
|
||||||
|
builder.field(name2, value2);
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries.
|
||||||
|
/// `debug_struct_fields_finish` is more general, but this is faster for 3 fields.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
|
||||||
|
pub fn debug_struct_field3_finish<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
name: &str,
|
||||||
|
name1: &str,
|
||||||
|
value1: &dyn Debug,
|
||||||
|
name2: &str,
|
||||||
|
value2: &dyn Debug,
|
||||||
|
name3: &str,
|
||||||
|
value3: &dyn Debug,
|
||||||
|
) -> Result {
|
||||||
|
let mut builder = builders::debug_struct_new(self, name);
|
||||||
|
builder.field(name1, value1);
|
||||||
|
builder.field(name2, value2);
|
||||||
|
builder.field(name3, value3);
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries.
|
||||||
|
/// `debug_struct_fields_finish` is more general, but this is faster for 4 fields.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
|
||||||
|
pub fn debug_struct_field4_finish<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
name: &str,
|
||||||
|
name1: &str,
|
||||||
|
value1: &dyn Debug,
|
||||||
|
name2: &str,
|
||||||
|
value2: &dyn Debug,
|
||||||
|
name3: &str,
|
||||||
|
value3: &dyn Debug,
|
||||||
|
name4: &str,
|
||||||
|
value4: &dyn Debug,
|
||||||
|
) -> Result {
|
||||||
|
let mut builder = builders::debug_struct_new(self, name);
|
||||||
|
builder.field(name1, value1);
|
||||||
|
builder.field(name2, value2);
|
||||||
|
builder.field(name3, value3);
|
||||||
|
builder.field(name4, value4);
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries.
|
||||||
|
/// `debug_struct_fields_finish` is more general, but this is faster for 5 fields.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
|
||||||
|
pub fn debug_struct_field5_finish<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
name: &str,
|
||||||
|
name1: &str,
|
||||||
|
value1: &dyn Debug,
|
||||||
|
name2: &str,
|
||||||
|
value2: &dyn Debug,
|
||||||
|
name3: &str,
|
||||||
|
value3: &dyn Debug,
|
||||||
|
name4: &str,
|
||||||
|
value4: &dyn Debug,
|
||||||
|
name5: &str,
|
||||||
|
value5: &dyn Debug,
|
||||||
|
) -> Result {
|
||||||
|
let mut builder = builders::debug_struct_new(self, name);
|
||||||
|
builder.field(name1, value1);
|
||||||
|
builder.field(name2, value2);
|
||||||
|
builder.field(name3, value3);
|
||||||
|
builder.field(name4, value4);
|
||||||
|
builder.field(name5, value5);
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries.
|
||||||
|
/// For the cases not covered by `debug_struct_field[12345]_finish`.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
|
||||||
|
pub fn debug_struct_fields_finish<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
name: &str,
|
||||||
|
names: &[&str],
|
||||||
|
values: &[&dyn Debug],
|
||||||
|
) -> Result {
|
||||||
|
assert_eq!(names.len(), values.len());
|
||||||
|
let mut builder = builders::debug_struct_new(self, name);
|
||||||
|
for (name, value) in iter::zip(names, values) {
|
||||||
|
builder.field(name, value);
|
||||||
|
}
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a `DebugTuple` builder designed to assist with creation of
|
/// Creates a `DebugTuple` builder designed to assist with creation of
|
||||||
/// `fmt::Debug` implementations for tuple structs.
|
/// `fmt::Debug` implementations for tuple structs.
|
||||||
///
|
///
|
||||||
@ -1995,6 +2119,108 @@ impl<'a> Formatter<'a> {
|
|||||||
builders::debug_tuple_new(self, name)
|
builders::debug_tuple_new(self, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries.
|
||||||
|
/// `debug_tuple_fields_finish` is more general, but this is faster for 1 field.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
|
||||||
|
pub fn debug_tuple_field1_finish<'b>(&'b mut self, name: &str, value1: &dyn Debug) -> Result {
|
||||||
|
let mut builder = builders::debug_tuple_new(self, name);
|
||||||
|
builder.field(value1);
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries.
|
||||||
|
/// `debug_tuple_fields_finish` is more general, but this is faster for 2 fields.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
|
||||||
|
pub fn debug_tuple_field2_finish<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
name: &str,
|
||||||
|
value1: &dyn Debug,
|
||||||
|
value2: &dyn Debug,
|
||||||
|
) -> Result {
|
||||||
|
let mut builder = builders::debug_tuple_new(self, name);
|
||||||
|
builder.field(value1);
|
||||||
|
builder.field(value2);
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries.
|
||||||
|
/// `debug_tuple_fields_finish` is more general, but this is faster for 3 fields.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
|
||||||
|
pub fn debug_tuple_field3_finish<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
name: &str,
|
||||||
|
value1: &dyn Debug,
|
||||||
|
value2: &dyn Debug,
|
||||||
|
value3: &dyn Debug,
|
||||||
|
) -> Result {
|
||||||
|
let mut builder = builders::debug_tuple_new(self, name);
|
||||||
|
builder.field(value1);
|
||||||
|
builder.field(value2);
|
||||||
|
builder.field(value3);
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries.
|
||||||
|
/// `debug_tuple_fields_finish` is more general, but this is faster for 4 fields.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
|
||||||
|
pub fn debug_tuple_field4_finish<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
name: &str,
|
||||||
|
value1: &dyn Debug,
|
||||||
|
value2: &dyn Debug,
|
||||||
|
value3: &dyn Debug,
|
||||||
|
value4: &dyn Debug,
|
||||||
|
) -> Result {
|
||||||
|
let mut builder = builders::debug_tuple_new(self, name);
|
||||||
|
builder.field(value1);
|
||||||
|
builder.field(value2);
|
||||||
|
builder.field(value3);
|
||||||
|
builder.field(value4);
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries.
|
||||||
|
/// `debug_tuple_fields_finish` is more general, but this is faster for 5 fields.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
|
||||||
|
pub fn debug_tuple_field5_finish<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
name: &str,
|
||||||
|
value1: &dyn Debug,
|
||||||
|
value2: &dyn Debug,
|
||||||
|
value3: &dyn Debug,
|
||||||
|
value4: &dyn Debug,
|
||||||
|
value5: &dyn Debug,
|
||||||
|
) -> Result {
|
||||||
|
let mut builder = builders::debug_tuple_new(self, name);
|
||||||
|
builder.field(value1);
|
||||||
|
builder.field(value2);
|
||||||
|
builder.field(value3);
|
||||||
|
builder.field(value4);
|
||||||
|
builder.field(value5);
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries.
|
||||||
|
/// For the cases not covered by `debug_tuple_field[12345]_finish`.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
|
||||||
|
pub fn debug_tuple_fields_finish<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
name: &str,
|
||||||
|
values: &[&dyn Debug],
|
||||||
|
) -> Result {
|
||||||
|
let mut builder = builders::debug_tuple_new(self, name);
|
||||||
|
for value in values {
|
||||||
|
builder.field(value);
|
||||||
|
}
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a `DebugList` builder designed to assist with creation of
|
/// Creates a `DebugList` builder designed to assist with creation of
|
||||||
/// `fmt::Debug` implementations for list-like structures.
|
/// `fmt::Debug` implementations for list-like structures.
|
||||||
///
|
///
|
||||||
|
Loading…
x
Reference in New Issue
Block a user