mirror of
https://github.com/rust-lang/rust.git
synced 2025-11-25 10:46:55 +00:00
184 lines
6.8 KiB
Rust
184 lines
6.8 KiB
Rust
use crate::deriving::generic::ty::*;
|
|
use crate::deriving::generic::*;
|
|
use crate::deriving::path_std;
|
|
|
|
use rustc_ast::ptr::P;
|
|
use rustc_ast::{self as ast, Expr, MetaItem};
|
|
use rustc_expand::base::{Annotatable, ExtCtxt};
|
|
use rustc_span::symbol::{sym, Ident, Symbol};
|
|
use rustc_span::Span;
|
|
|
|
pub fn expand_deriving_debug(
|
|
cx: &mut ExtCtxt<'_>,
|
|
span: Span,
|
|
mitem: &MetaItem,
|
|
item: &Annotatable,
|
|
push: &mut dyn FnMut(Annotatable),
|
|
) {
|
|
// &mut ::std::fmt::Formatter
|
|
let fmtr = Ref(Box::new(Path(path_std!(fmt::Formatter))), ast::Mutability::Mut);
|
|
|
|
let trait_def = TraitDef {
|
|
span,
|
|
attributes: Vec::new(),
|
|
path: path_std!(fmt::Debug),
|
|
additional_bounds: Vec::new(),
|
|
generics: Bounds::empty(),
|
|
supports_unions: false,
|
|
methods: vec![MethodDef {
|
|
name: sym::fmt,
|
|
generics: Bounds::empty(),
|
|
explicit_self: true,
|
|
args: vec![(fmtr, sym::f)],
|
|
ret_ty: Path(path_std!(fmt::Result)),
|
|
attributes: Vec::new(),
|
|
unify_fieldless_variants: false,
|
|
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
|
show_substructure(a, b, c)
|
|
})),
|
|
}],
|
|
associated_types: Vec::new(),
|
|
};
|
|
trait_def.expand(cx, mitem, item, push)
|
|
}
|
|
|
|
fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
|
|
let (ident, vdata, fields) = match substr.fields {
|
|
Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
|
|
EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
|
|
EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
|
|
cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
|
|
}
|
|
};
|
|
|
|
// We want to make sure we have the ctxt set so that we can use unstable methods
|
|
let span = cx.with_def_site_ctxt(span);
|
|
let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked));
|
|
let fmt = substr.nonself_args[0].clone();
|
|
|
|
// Struct and tuples are similar enough that we use the same code for both,
|
|
// with some extra pieces for structs due to the field names.
|
|
let (is_struct, args_per_field) = match vdata {
|
|
ast::VariantData::Unit(..) => {
|
|
// Special fast path for unit variants.
|
|
assert!(fields.is_empty());
|
|
(false, 0)
|
|
}
|
|
ast::VariantData::Tuple(..) => (false, 1),
|
|
ast::VariantData::Struct(..) => (true, 2),
|
|
};
|
|
|
|
// The number of fields that can be handled without an array.
|
|
const CUTOFF: usize = 5;
|
|
|
|
if fields.is_empty() {
|
|
// Special case for no fields.
|
|
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)]);
|
|
|
|
let mut args = Vec::with_capacity(2 + fields.len() * args_per_field);
|
|
args.extend([fmt, name]);
|
|
for i in 0..fields.len() {
|
|
let field = &fields[i];
|
|
if is_struct {
|
|
let name = cx.expr_lit(
|
|
field.span,
|
|
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 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))
|
|
}
|
|
}
|