Rollup merge of #59170 - varkor:const-generics-rustdoc, r=QuietMisdreavus,eddyb

Add const generics to rustdoc

Split out from #53645. This work is a collaborative effort with @yodaldevoid.

The `FIXME`s are waiting on a refactor to `LazyConst`. I'll address these in a follow up, but I thought it would be better to implement the rest now to avoid bitrot.

r? @QuietMisdreavus
This commit is contained in:
Mazdak Farrokhzad 2019-03-22 19:31:18 +01:00 committed by GitHub
commit 4c79967760
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 156 additions and 117 deletions

View File

@ -591,12 +591,12 @@ impl<'a> State<'a> {
self.s.word(";")?;
self.end()?; // end the outer cbox
}
hir::ItemKind::Fn(ref decl, header, ref typarams, body) => {
hir::ItemKind::Fn(ref decl, header, ref param_names, body) => {
self.head("")?;
self.print_fn(decl,
header,
Some(item.ident.name),
typarams,
param_names,
&item.vis,
&[],
Some(body))?;

View File

@ -435,7 +435,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
let new_ty = match &poly_trait.trait_ {
&Type::ResolvedPath {
ref path,
ref typarams,
ref param_names,
ref did,
ref is_generic,
} => {
@ -444,7 +444,13 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
.expect("segments were empty");
let (old_input, old_output) = match last_segment.args {
GenericArgs::AngleBracketed { types, .. } => (types, None),
GenericArgs::AngleBracketed { args, .. } => {
let types = args.iter().filter_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty.clone()),
_ => None,
}).collect();
(types, None)
}
GenericArgs::Parenthesized { inputs, output, .. } => {
(inputs, output)
}
@ -469,7 +475,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
Type::ResolvedPath {
path: new_path,
typarams: typarams.clone(),
param_names: param_names.clone(),
did: did.clone(),
is_generic: *is_generic,
}
@ -669,7 +675,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
match **trait_ {
Type::ResolvedPath {
path: ref trait_path,
ref typarams,
ref param_names,
ref did,
ref is_generic,
} => {
@ -724,7 +730,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
PolyTrait {
trait_: Type::ResolvedPath {
path: new_trait_path,
typarams: typarams.clone(),
param_names: param_names.clone(),
did: did.clone(),
is_generic: *is_generic,
},

View File

@ -17,11 +17,11 @@ use rustc::middle::resolve_lifetime as rl;
use rustc::middle::lang_items;
use rustc::middle::stability;
use rustc::mir::interpret::{GlobalId, ConstValue};
use rustc::hir::{self, GenericArg, HirVec};
use rustc::hir::{self, HirVec};
use rustc::hir::def::{self, Def, CtorKind};
use rustc::hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc::hir::map::DisambiguatedDefPathData;
use rustc::ty::subst::{Kind, InternalSubsts, SubstsRef};
use rustc::ty::subst::{Kind, InternalSubsts, SubstsRef, UnpackedKind};
use rustc::ty::{self, DefIdTree, TyCtxt, Region, RegionVid, Ty, AdtKind};
use rustc::ty::fold::TypeFolder;
use rustc::ty::layout::VariantIdx;
@ -1057,7 +1057,7 @@ impl GenericBound {
GenericBound::TraitBound(PolyTrait {
trait_: ResolvedPath {
path,
typarams: None,
param_names: None,
did,
is_generic: false,
},
@ -1101,24 +1101,37 @@ impl Clean<GenericBound> for hir::GenericBound {
}
}
fn external_generic_args(cx: &DocContext<'_>, trait_did: Option<DefId>, has_self: bool,
bindings: Vec<TypeBinding>, substs: SubstsRef<'_>) -> GenericArgs {
let lifetimes = substs.regions().filter_map(|v| v.clean(cx)).collect();
let types = substs.types().skip(has_self as usize).collect::<Vec<_>>();
fn external_generic_args(
cx: &DocContext<'_>,
trait_did: Option<DefId>,
has_self: bool,
bindings: Vec<TypeBinding>,
substs: SubstsRef<'_>,
) -> GenericArgs {
let mut skip_self = has_self;
let mut ty_sty = None;
let args: Vec<_> = substs.iter().filter_map(|kind| match kind.unpack() {
UnpackedKind::Lifetime(lt) => {
lt.clean(cx).and_then(|lt| Some(GenericArg::Lifetime(lt)))
}
UnpackedKind::Type(_) if skip_self => {
skip_self = false;
None
}
UnpackedKind::Type(ty) => {
ty_sty = Some(&ty.sty);
Some(GenericArg::Type(ty.clean(cx)))
}
UnpackedKind::Const(ct) => Some(GenericArg::Const(ct.clean(cx))),
}).collect();
match trait_did {
// Attempt to sugar an external path like Fn<(A, B,), C> to Fn(A, B) -> C
Some(did) if cx.tcx.lang_items().fn_trait_kind(did).is_some() => {
assert_eq!(types.len(), 1);
let inputs = match types[0].sty {
ty::Tuple(ref tys) => tys.iter().map(|t| t.clean(cx)).collect(),
_ => {
return GenericArgs::AngleBracketed {
lifetimes,
types: types.clean(cx),
bindings,
}
}
assert!(ty_sty.is_some());
let inputs = match ty_sty {
Some(ty::Tuple(ref tys)) => tys.iter().map(|t| t.clean(cx)).collect(),
_ => return GenericArgs::AngleBracketed { args, bindings },
};
let output = None;
// FIXME(#20299) return type comes from a projection now
@ -1126,17 +1139,10 @@ fn external_generic_args(cx: &DocContext<'_>, trait_did: Option<DefId>, has_self
// ty::Tuple(ref v) if v.is_empty() => None, // -> ()
// _ => Some(types[1].clean(cx))
// };
GenericArgs::Parenthesized {
inputs,
output,
}
GenericArgs::Parenthesized { inputs, output }
},
_ => {
GenericArgs::AngleBracketed {
lifetimes,
types: types.clean(cx),
bindings,
}
GenericArgs::AngleBracketed { args, bindings }
}
}
}
@ -1188,7 +1194,7 @@ impl<'a, 'tcx> Clean<GenericBound> for (&'a ty::TraitRef<'tcx>, Vec<TypeBinding>
PolyTrait {
trait_: ResolvedPath {
path,
typarams: None,
param_names: None,
did: trait_ref.def_id,
is_generic: false,
},
@ -1474,14 +1480,14 @@ impl GenericParamDef {
}
}
impl<'tcx> Clean<GenericParamDef> for ty::GenericParamDef {
impl Clean<GenericParamDef> for ty::GenericParamDef {
fn clean(&self, cx: &DocContext<'_>) -> GenericParamDef {
let (name, kind) = match self.kind {
ty::GenericParamDefKind::Lifetime => {
(self.name.to_string(), GenericParamDefKind::Lifetime)
}
ty::GenericParamDefKind::Type { has_default, .. } => {
cx.renderinfo.borrow_mut().external_typarams
cx.renderinfo.borrow_mut().external_param_names
.insert(self.def_id, self.name.clean(cx));
let default = if has_default {
Some(cx.tcx.type_of(self.def_id).clean(cx))
@ -1496,7 +1502,10 @@ impl<'tcx> Clean<GenericParamDef> for ty::GenericParamDef {
})
}
ty::GenericParamDefKind::Const { .. } => {
unimplemented!() // FIXME(const_generics)
(self.name.clean(cx), GenericParamDefKind::Const {
did: self.def_id,
ty: cx.tcx.type_of(self.def_id).clean(cx),
})
}
};
@ -1697,9 +1706,7 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics,
.flat_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => Some(param.clean(cx)),
ty::GenericParamDefKind::Type { .. } => None,
ty::GenericParamDefKind::Const { .. } => {
unimplemented!() // FIXME(const_generics)
}
ty::GenericParamDefKind::Const { .. } => Some(param.clean(cx)),
}).chain(simplify::ty_params(stripped_typarams).into_iter())
.collect(),
where_predicates: simplify::where_clauses(cx, where_predicates),
@ -2260,7 +2267,7 @@ pub enum Type {
/// Structs/enums/traits (most that'd be an `hir::TyKind::Path`).
ResolvedPath {
path: Path,
typarams: Option<Vec<GenericBound>>,
param_names: Option<Vec<GenericBound>>,
did: DefId,
/// `true` if is a `T::Name` path for associated types.
is_generic: bool,
@ -2381,12 +2388,15 @@ impl Type {
}
}
pub fn generics(&self) -> Option<&[Type]> {
pub fn generics(&self) -> Option<Vec<Type>> {
match *self {
ResolvedPath { ref path, .. } => {
path.segments.last().and_then(|seg| {
if let GenericArgs::AngleBracketed { ref types, .. } = seg.args {
Some(&**types)
if let GenericArgs::AngleBracketed { ref args, .. } = seg.args {
Some(args.iter().filter_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty.clone()),
_ => None,
}).collect())
} else {
None
}
@ -2722,7 +2732,7 @@ impl Clean<Type> for hir::Ty {
}
TyKind::TraitObject(ref bounds, ref lifetime) => {
match bounds[0].clean(cx).trait_ {
ResolvedPath { path, typarams: None, did, is_generic } => {
ResolvedPath { path, param_names: None, did, is_generic } => {
let mut bounds: Vec<self::GenericBound> = bounds[1..].iter().map(|bound| {
self::GenericBound::TraitBound(bound.clean(cx),
hir::TraitBoundModifier::None)
@ -2730,7 +2740,7 @@ impl Clean<Type> for hir::Ty {
if !lifetime.is_elided() {
bounds.push(self::GenericBound::Outlives(lifetime.clean(cx)));
}
ResolvedPath { path, typarams: Some(bounds), did, is_generic, }
ResolvedPath { path, param_names: Some(bounds), did, is_generic, }
}
_ => Infer // shouldn't happen
}
@ -2797,7 +2807,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
None, false, vec![], substs);
ResolvedPath {
path,
typarams: None,
param_names: None,
did,
is_generic: false,
}
@ -2808,7 +2818,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
None, false, vec![], InternalSubsts::empty());
ResolvedPath {
path: path,
typarams: None,
param_names: None,
did: did,
is_generic: false,
}
@ -2829,8 +2839,8 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
inline::record_extern_fqn(cx, did, TypeKind::Trait);
let mut typarams = vec![];
reg.clean(cx).map(|b| typarams.push(GenericBound::Outlives(b)));
let mut param_names = vec![];
reg.clean(cx).map(|b| param_names.push(GenericBound::Outlives(b)));
for did in dids {
let empty = cx.tcx.intern_substs(&[]);
let path = external_path(cx, &cx.tcx.item_name(did).as_str(),
@ -2839,13 +2849,13 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
let bound = GenericBound::TraitBound(PolyTrait {
trait_: ResolvedPath {
path,
typarams: None,
param_names: None,
did,
is_generic: false,
},
generic_params: Vec::new(),
}, hir::TraitBoundModifier::None);
typarams.push(bound);
param_names.push(bound);
}
let mut bindings = vec![];
@ -2860,7 +2870,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
false, bindings, substs);
ResolvedPath {
path,
typarams: Some(typarams),
param_names: Some(param_names),
did,
is_generic: false,
}
@ -2937,6 +2947,15 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
}
}
impl<'tcx> Clean<Constant> for ty::Const<'tcx> {
fn clean(&self, cx: &DocContext<'_>) -> Constant {
Constant {
type_: self.ty.clean(cx),
expr: format!("{:?}", self.val), // FIXME(const_generics)
}
}
}
impl Clean<Item> for hir::StructField {
fn clean(&self, cx: &DocContext<'_>) -> Item {
let local_did = cx.tcx.hir().local_def_id_from_hir_id(self.hir_id);
@ -3244,11 +3263,27 @@ impl Clean<Path> for hir::Path {
}
}
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Debug, Hash)]
pub enum GenericArg {
Lifetime(Lifetime),
Type(Type),
Const(Constant),
}
impl fmt::Display for GenericArg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GenericArg::Lifetime(lt) => lt.fmt(f),
GenericArg::Type(ty) => ty.fmt(f),
GenericArg::Const(ct) => ct.fmt(f),
}
}
}
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Debug, Hash)]
pub enum GenericArgs {
AngleBracketed {
lifetimes: Vec<Lifetime>,
types: Vec<Type>,
args: Vec<GenericArg>,
bindings: Vec<TypeBinding>,
},
Parenthesized {
@ -3266,27 +3301,19 @@ impl Clean<GenericArgs> for hir::GenericArgs {
output: if output != Type::Tuple(Vec::new()) { Some(output) } else { None }
}
} else {
let (mut lifetimes, mut types) = (vec![], vec![]);
let mut elided_lifetimes = true;
for arg in &self.args {
match arg {
GenericArg::Lifetime(lt) => {
if !lt.is_elided() {
elided_lifetimes = false;
}
lifetimes.push(lt.clean(cx));
}
GenericArg::Type(ty) => {
types.push(ty.clean(cx));
}
GenericArg::Const(..) => {
unimplemented!() // FIXME(const_generics)
}
}
}
let elide_lifetimes = self.args.iter().all(|arg| match arg {
hir::GenericArg::Lifetime(lt) => lt.is_elided(),
_ => true,
});
GenericArgs::AngleBracketed {
lifetimes: if elided_lifetimes { vec![] } else { lifetimes },
types,
args: self.args.iter().filter_map(|arg| match arg {
hir::GenericArg::Lifetime(lt) if !elide_lifetimes => {
Some(GenericArg::Lifetime(lt.clean(cx)))
}
hir::GenericArg::Lifetime(_) => None,
hir::GenericArg::Type(ty) => Some(GenericArg::Type(ty.clean(cx))),
hir::GenericArg::Const(ct) => Some(GenericArg::Const(ct.clean(cx))),
}).collect(),
bindings: self.bindings.clean(cx),
}
}
@ -3310,8 +3337,8 @@ impl Clean<PathSegment> for hir::PathSegment {
fn strip_type(ty: Type) -> Type {
match ty {
Type::ResolvedPath { path, typarams, did, is_generic } => {
Type::ResolvedPath { path: strip_path(&path), typarams, did, is_generic }
Type::ResolvedPath { path, param_names, did, is_generic } => {
Type::ResolvedPath { path: strip_path(&path), param_names, did, is_generic }
}
Type::Tuple(inner_tys) => {
Type::Tuple(inner_tys.iter().map(|t| strip_type(t.clone())).collect())
@ -3338,9 +3365,8 @@ fn strip_path(path: &Path) -> Path {
PathSegment {
name: s.name.clone(),
args: GenericArgs::AngleBracketed {
lifetimes: Vec::new(),
types: Vec::new(),
bindings: Vec::new(),
args: vec![],
bindings: vec![],
}
}
}).collect();
@ -3491,7 +3517,7 @@ impl Clean<Item> for doctree::Static {
}
}
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
pub struct Constant {
pub type_: Type,
pub expr: String,
@ -3971,7 +3997,7 @@ fn resolve_type(cx: &DocContext<'_>,
_ => false,
};
let did = register_def(&*cx, path.def);
ResolvedPath { path: path, typarams: None, did: did, is_generic: is_generic }
ResolvedPath { path: path, param_names: None, did: did, is_generic: is_generic }
}
pub fn register_def(cx: &DocContext<'_>, def: Def) -> DefId {
@ -4397,9 +4423,9 @@ impl From<GenericBound> for SimpleBound {
match bound.clone() {
GenericBound::Outlives(l) => SimpleBound::Outlives(l),
GenericBound::TraitBound(t, mod_) => match t.trait_ {
Type::ResolvedPath { path, typarams, .. } => {
Type::ResolvedPath { path, param_names, .. } => {
SimpleBound::TraitBound(path.segments,
typarams
param_names
.map_or_else(|| Vec::new(), |v| v.iter()
.map(|p| SimpleBound::from(p.clone()))
.collect()),

View File

@ -236,8 +236,16 @@ impl<'tcx> DocContext<'tcx> {
ty::GenericParamDefKind::Type { .. } => {
args.push(hir::GenericArg::Type(self.ty_param_to_ty(param.clone())));
}
ty::GenericParamDefKind::Const { .. } => {
unimplemented!() // FIXME(const_generics)
ty::GenericParamDefKind::Const => {
args.push(hir::GenericArg::Const(hir::ConstArg {
value: hir::AnonConst {
hir_id: hir::DUMMY_HIR_ID,
body: hir::BodyId {
hir_id: hir::DUMMY_HIR_ID,
}
},
span: DUMMY_SP,
}))
}
}
}

View File

@ -260,6 +260,14 @@ impl fmt::Display for clean::Lifetime {
}
}
impl fmt::Display for clean::Constant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.expr, f)?;
f.write_str(": ")?;
fmt::Display::fmt(&self.type_, f)
}
}
impl fmt::Display for clean::PolyTrait {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.generic_params.is_empty() {
@ -301,32 +309,23 @@ impl fmt::Display for clean::GenericBound {
impl fmt::Display for clean::GenericArgs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
clean::GenericArgs::AngleBracketed {
ref lifetimes, ref types, ref bindings
} => {
if !lifetimes.is_empty() || !types.is_empty() || !bindings.is_empty() {
clean::GenericArgs::AngleBracketed { ref args, ref bindings } => {
if !args.is_empty() || !bindings.is_empty() {
if f.alternate() {
f.write_str("<")?;
} else {
f.write_str("&lt;")?;
}
let mut comma = false;
for lifetime in lifetimes {
if comma {
f.write_str(", ")?;
}
comma = true;
write!(f, "{}", *lifetime)?;
}
for ty in types {
for arg in args {
if comma {
f.write_str(", ")?;
}
comma = true;
if f.alternate() {
write!(f, "{:#}", *ty)?;
write!(f, "{:#}", *arg)?;
} else {
write!(f, "{}", *ty)?;
write!(f, "{}", *arg)?;
}
}
for binding in bindings {
@ -522,8 +521,8 @@ fn primitive_link(f: &mut fmt::Formatter<'_>,
/// Helper to render type parameters
fn tybounds(w: &mut fmt::Formatter<'_>,
typarams: &Option<Vec<clean::GenericBound>>) -> fmt::Result {
match *typarams {
param_names: &Option<Vec<clean::GenericBound>>) -> fmt::Result {
match *param_names {
Some(ref params) => {
for param in params {
write!(w, " + ")?;
@ -560,13 +559,13 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
clean::Generic(ref name) => {
f.write_str(name)
}
clean::ResolvedPath{ did, ref typarams, ref path, is_generic } => {
if typarams.is_some() {
clean::ResolvedPath{ did, ref param_names, ref path, is_generic } => {
if param_names.is_some() {
f.write_str("dyn ")?;
}
// Paths like T::Output and Self::Output should be rendered with all segments
resolved_path(f, did, path, is_generic, use_absolute)?;
tybounds(f, typarams)
tybounds(f, param_names)
}
clean::Infer => write!(f, "_"),
clean::Primitive(prim) => primitive_link(f, prim, prim.as_str()),
@ -664,7 +663,7 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
}
}
}
clean::ResolvedPath { typarams: Some(ref v), .. } if !v.is_empty() => {
clean::ResolvedPath { param_names: Some(ref v), .. } if !v.is_empty() => {
write!(f, "{}{}{}(", amp, lt, m)?;
fmt_type(&ty, f, use_absolute)?;
write!(f, ")")
@ -718,7 +717,7 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
// the ugliness comes from inlining across crates where
// everything comes in as a fully resolved QPath (hard to
// look at).
box clean::ResolvedPath { did, ref typarams, .. } => {
box clean::ResolvedPath { did, ref param_names, .. } => {
match href(did) {
Some((ref url, _, ref path)) if !f.alternate() => {
write!(f,
@ -732,8 +731,8 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
_ => write!(f, "{}", name)?,
}
// FIXME: `typarams` are not rendered, and this seems bad?
drop(typarams);
// FIXME: `param_names` are not rendered, and this seems bad?
drop(param_names);
Ok(())
}
_ => {
@ -772,7 +771,7 @@ fn fmt_impl(i: &clean::Impl,
fmt::Display::fmt(ty, f)?;
} else {
match *ty {
clean::ResolvedPath { typarams: None, ref path, is_generic: false, .. } => {
clean::ResolvedPath { param_names: None, ref path, is_generic: false, .. } => {
let last = path.segments.last().unwrap();
fmt::Display::fmt(&last.name, f)?;
fmt::Display::fmt(&last.args, f)?;

View File

@ -271,7 +271,7 @@ pub struct Cache {
/// Mapping of typaram ids to the name of the type parameter. This is used
/// when pretty-printing a type (so pretty-printing doesn't have to
/// painfully maintain a context like this)
pub typarams: FxHashMap<DefId, String>,
pub param_names: FxHashMap<DefId, String>,
/// Maps a type ID to all known implementations for that type. This is only
/// recognized for intra-crate `ResolvedPath` types, and is used to print
@ -368,7 +368,7 @@ pub struct Cache {
pub struct RenderInfo {
pub inlined: FxHashSet<DefId>,
pub external_paths: crate::core::ExternalPaths,
pub external_typarams: FxHashMap<DefId, String>,
pub external_param_names: FxHashMap<DefId, String>,
pub exact_paths: FxHashMap<DefId, Vec<String>>,
pub access_levels: AccessLevels<DefId>,
pub deref_trait_did: Option<DefId>,
@ -601,7 +601,7 @@ pub fn run(mut krate: clean::Crate,
let RenderInfo {
inlined: _,
external_paths,
external_typarams,
external_param_names,
exact_paths,
access_levels,
deref_trait_did,
@ -635,7 +635,7 @@ pub fn run(mut krate: clean::Crate,
deref_mut_trait_did,
owned_box_did,
masked_crates: mem::replace(&mut krate.masked_crates, Default::default()),
typarams: external_typarams,
param_names: external_param_names,
aliases: Default::default(),
};
@ -1751,7 +1751,7 @@ impl<'a> Cache {
clean::GenericParamDefKind::Lifetime => {}
clean::GenericParamDefKind::Type { did, .. } |
clean::GenericParamDefKind::Const { did, .. } => {
self.typarams.insert(did, param.name.clone());
self.param_names.insert(did, param.name.clone());
}
}
}

View File

@ -1263,13 +1263,13 @@ impl<'a> State<'a> {
self.s.word(";")?;
self.end()?; // end the outer cbox
}
ast::ItemKind::Fn(ref decl, header, ref typarams, ref body) => {
ast::ItemKind::Fn(ref decl, header, ref param_names, ref body) => {
self.head("")?;
self.print_fn(
decl,
header,
Some(item.ident),
typarams,
param_names,
&item.vis
)?;
self.s.word(" ")?;