| // A signature is a string representation of an item's type signature, excluding |
| // any body. It also includes ids for any defs or refs in the signature. For |
| // example: |
| // |
| // ``` |
| // fn foo(x: String) { |
| // println!("{}", x); |
| // } |
| // ``` |
| // The signature string is something like "fn foo(x: String) {}" and the signature |
| // will have defs for `foo` and `x` and a ref for `String`. |
| // |
| // All signature text should parse in the correct context (i.e., in a module or |
| // impl, etc.). Clients may want to trim trailing `{}` or `;`. The text of a |
| // signature is not guaranteed to be stable (it may improve or change as the |
| // syntax changes, or whitespace or punctuation may change). It is also likely |
| // not to be pretty - no attempt is made to prettify the text. It is recommended |
| // that clients run the text through Rustfmt. |
| // |
| // This module generates Signatures for items by walking the AST and looking up |
| // references. |
| // |
| // Signatures do not include visibility info. I'm not sure if this is a feature |
| // or an omission (FIXME). |
| // |
| // FIXME where clauses need implementing, defs/refs in generics are mostly missing. |
| |
| use crate::{id_from_def_id, id_from_hir_id, SaveContext}; |
| |
| use rls_data::{SigElement, Signature}; |
| |
| use rustc_ast::Mutability; |
| use rustc_hir as hir; |
| use rustc_hir::def::{DefKind, Res}; |
| use rustc_hir_pretty::id_to_string; |
| use rustc_hir_pretty::{bounds_to_string, path_segment_to_string, path_to_string, ty_to_string}; |
| use rustc_span::symbol::{Ident, Symbol}; |
| |
| pub fn item_signature(item: &hir::Item<'_>, scx: &SaveContext<'_>) -> Option<Signature> { |
| if !scx.config.signatures { |
| return None; |
| } |
| item.make(0, None, scx).ok() |
| } |
| |
| pub fn foreign_item_signature( |
| item: &hir::ForeignItem<'_>, |
| scx: &SaveContext<'_>, |
| ) -> Option<Signature> { |
| if !scx.config.signatures { |
| return None; |
| } |
| item.make(0, None, scx).ok() |
| } |
| |
| /// Signature for a struct or tuple field declaration. |
| /// Does not include a trailing comma. |
| pub fn field_signature(field: &hir::FieldDef<'_>, scx: &SaveContext<'_>) -> Option<Signature> { |
| if !scx.config.signatures { |
| return None; |
| } |
| field.make(0, None, scx).ok() |
| } |
| |
| /// Does not include a trailing comma. |
| pub fn variant_signature(variant: &hir::Variant<'_>, scx: &SaveContext<'_>) -> Option<Signature> { |
| if !scx.config.signatures { |
| return None; |
| } |
| variant.make(0, None, scx).ok() |
| } |
| |
| pub fn method_signature( |
| id: hir::HirId, |
| ident: Ident, |
| generics: &hir::Generics<'_>, |
| m: &hir::FnSig<'_>, |
| scx: &SaveContext<'_>, |
| ) -> Option<Signature> { |
| if !scx.config.signatures { |
| return None; |
| } |
| make_method_signature(id, ident, generics, m, scx).ok() |
| } |
| |
| pub fn assoc_const_signature( |
| id: hir::HirId, |
| ident: Symbol, |
| ty: &hir::Ty<'_>, |
| default: Option<&hir::Expr<'_>>, |
| scx: &SaveContext<'_>, |
| ) -> Option<Signature> { |
| if !scx.config.signatures { |
| return None; |
| } |
| make_assoc_const_signature(id, ident, ty, default, scx).ok() |
| } |
| |
| pub fn assoc_type_signature( |
| id: hir::HirId, |
| ident: Ident, |
| bounds: Option<hir::GenericBounds<'_>>, |
| default: Option<&hir::Ty<'_>>, |
| scx: &SaveContext<'_>, |
| ) -> Option<Signature> { |
| if !scx.config.signatures { |
| return None; |
| } |
| make_assoc_type_signature(id, ident, bounds, default, scx).ok() |
| } |
| |
| type Result = std::result::Result<Signature, &'static str>; |
| |
| trait Sig { |
| fn make(&self, offset: usize, id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result; |
| } |
| |
| fn extend_sig( |
| mut sig: Signature, |
| text: String, |
| defs: Vec<SigElement>, |
| refs: Vec<SigElement>, |
| ) -> Signature { |
| sig.text = text; |
| sig.defs.extend(defs.into_iter()); |
| sig.refs.extend(refs.into_iter()); |
| sig |
| } |
| |
| fn replace_text(mut sig: Signature, text: String) -> Signature { |
| sig.text = text; |
| sig |
| } |
| |
| fn merge_sigs(text: String, sigs: Vec<Signature>) -> Signature { |
| let mut result = Signature { text, defs: vec![], refs: vec![] }; |
| |
| let (defs, refs): (Vec<_>, Vec<_>) = sigs.into_iter().map(|s| (s.defs, s.refs)).unzip(); |
| |
| result.defs.extend(defs.into_iter().flat_map(|ds| ds.into_iter())); |
| result.refs.extend(refs.into_iter().flat_map(|rs| rs.into_iter())); |
| |
| result |
| } |
| |
| fn text_sig(text: String) -> Signature { |
| Signature { text, defs: vec![], refs: vec![] } |
| } |
| |
| impl<'hir> Sig for hir::Ty<'hir> { |
| fn make(&self, offset: usize, _parent_id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result { |
| let id = Some(self.hir_id); |
| match self.kind { |
| hir::TyKind::Slice(ref ty) => { |
| let nested = ty.make(offset + 1, id, scx)?; |
| let text = format!("[{}]", nested.text); |
| Ok(replace_text(nested, text)) |
| } |
| hir::TyKind::Ptr(ref mt) => { |
| let prefix = match mt.mutbl { |
| hir::Mutability::Mut => "*mut ", |
| hir::Mutability::Not => "*const ", |
| }; |
| let nested = mt.ty.make(offset + prefix.len(), id, scx)?; |
| let text = format!("{}{}", prefix, nested.text); |
| Ok(replace_text(nested, text)) |
| } |
| hir::TyKind::Rptr(ref lifetime, ref mt) => { |
| let mut prefix = "&".to_owned(); |
| prefix.push_str(&lifetime.name.ident().to_string()); |
| prefix.push(' '); |
| if let hir::Mutability::Mut = mt.mutbl { |
| prefix.push_str("mut "); |
| }; |
| |
| let nested = mt.ty.make(offset + prefix.len(), id, scx)?; |
| let text = format!("{}{}", prefix, nested.text); |
| Ok(replace_text(nested, text)) |
| } |
| hir::TyKind::Never => Ok(text_sig("!".to_owned())), |
| hir::TyKind::Tup(ts) => { |
| let mut text = "(".to_owned(); |
| let mut defs = vec![]; |
| let mut refs = vec![]; |
| for t in ts { |
| let nested = t.make(offset + text.len(), id, scx)?; |
| text.push_str(&nested.text); |
| text.push(','); |
| defs.extend(nested.defs.into_iter()); |
| refs.extend(nested.refs.into_iter()); |
| } |
| text.push(')'); |
| Ok(Signature { text, defs, refs }) |
| } |
| hir::TyKind::BareFn(ref f) => { |
| let mut text = String::new(); |
| if !f.generic_params.is_empty() { |
| // FIXME defs, bounds on lifetimes |
| text.push_str("for<"); |
| text.push_str( |
| &f.generic_params |
| .iter() |
| .filter_map(|param| match param.kind { |
| hir::GenericParamKind::Lifetime { .. } => { |
| Some(param.name.ident().to_string()) |
| } |
| _ => None, |
| }) |
| .collect::<Vec<_>>() |
| .join(", "), |
| ); |
| text.push('>'); |
| } |
| |
| if let hir::Unsafety::Unsafe = f.unsafety { |
| text.push_str("unsafe "); |
| } |
| text.push_str("fn("); |
| |
| let mut defs = vec![]; |
| let mut refs = vec![]; |
| for i in f.decl.inputs { |
| let nested = i.make(offset + text.len(), Some(i.hir_id), scx)?; |
| text.push_str(&nested.text); |
| text.push(','); |
| defs.extend(nested.defs.into_iter()); |
| refs.extend(nested.refs.into_iter()); |
| } |
| text.push(')'); |
| if let hir::FnRetTy::Return(ref t) = f.decl.output { |
| text.push_str(" -> "); |
| let nested = t.make(offset + text.len(), None, scx)?; |
| text.push_str(&nested.text); |
| text.push(','); |
| defs.extend(nested.defs.into_iter()); |
| refs.extend(nested.refs.into_iter()); |
| } |
| |
| Ok(Signature { text, defs, refs }) |
| } |
| hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => path.make(offset, id, scx), |
| hir::TyKind::Path(hir::QPath::Resolved(Some(ref qself), ref path)) => { |
| let nested_ty = qself.make(offset + 1, id, scx)?; |
| let prefix = format!( |
| "<{} as {}>::", |
| nested_ty.text, |
| path_segment_to_string(&path.segments[0]) |
| ); |
| |
| let name = path_segment_to_string(path.segments.last().ok_or("Bad path")?); |
| let res = scx.get_path_res(id.ok_or("Missing id for Path")?); |
| let id = id_from_def_id(res.def_id()); |
| if path.segments.len() == 2 { |
| let start = offset + prefix.len(); |
| let end = start + name.len(); |
| |
| Ok(Signature { |
| text: prefix + &name, |
| defs: vec![], |
| refs: vec![SigElement { id, start, end }], |
| }) |
| } else { |
| let start = offset + prefix.len() + 5; |
| let end = start + name.len(); |
| // FIXME should put the proper path in there, not ellipsis. |
| Ok(Signature { |
| text: prefix + "...::" + &name, |
| defs: vec![], |
| refs: vec![SigElement { id, start, end }], |
| }) |
| } |
| } |
| hir::TyKind::Path(hir::QPath::TypeRelative(ty, segment)) => { |
| let nested_ty = ty.make(offset + 1, id, scx)?; |
| let prefix = format!("<{}>::", nested_ty.text); |
| |
| let name = path_segment_to_string(segment); |
| let res = scx.get_path_res(id.ok_or("Missing id for Path")?); |
| let id = id_from_def_id(res.def_id()); |
| |
| let start = offset + prefix.len(); |
| let end = start + name.len(); |
| Ok(Signature { |
| text: prefix + &name, |
| defs: vec![], |
| refs: vec![SigElement { id, start, end }], |
| }) |
| } |
| hir::TyKind::Path(hir::QPath::LangItem(lang_item, _)) => { |
| Ok(text_sig(format!("#[lang = \"{}\"]", lang_item.name()))) |
| } |
| hir::TyKind::TraitObject(bounds, ..) => { |
| // FIXME recurse into bounds |
| let bounds: Vec<hir::GenericBound<'_>> = bounds |
| .iter() |
| .map(|hir::PolyTraitRef { bound_generic_params, trait_ref, span }| { |
| hir::GenericBound::Trait( |
| hir::PolyTraitRef { |
| bound_generic_params, |
| trait_ref: hir::TraitRef { |
| path: trait_ref.path, |
| hir_ref_id: trait_ref.hir_ref_id, |
| }, |
| span: *span, |
| }, |
| hir::TraitBoundModifier::None, |
| ) |
| }) |
| .collect(); |
| let nested = bounds_to_string(&bounds); |
| Ok(text_sig(nested)) |
| } |
| hir::TyKind::Array(ref ty, ref anon_const) => { |
| let nested_ty = ty.make(offset + 1, id, scx)?; |
| let expr = id_to_string(&scx.tcx.hir(), anon_const.body.hir_id).replace('\n', " "); |
| let text = format!("[{}; {}]", nested_ty.text, expr); |
| Ok(replace_text(nested_ty, text)) |
| } |
| hir::TyKind::OpaqueDef(item_id, _) => { |
| let item = scx.tcx.hir().item(item_id); |
| item.make(offset, Some(item_id.hir_id()), scx) |
| } |
| hir::TyKind::Typeof(_) | hir::TyKind::Infer | hir::TyKind::Err => Err("Ty"), |
| } |
| } |
| } |
| |
| impl<'hir> Sig for hir::Item<'hir> { |
| fn make(&self, offset: usize, _parent_id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result { |
| let id = Some(self.hir_id()); |
| |
| match self.kind { |
| hir::ItemKind::Static(ref ty, m, ref body) => { |
| let mut text = "static ".to_owned(); |
| if m == hir::Mutability::Mut { |
| text.push_str("mut "); |
| } |
| let name = self.ident.to_string(); |
| let defs = vec![SigElement { |
| id: id_from_def_id(self.def_id.to_def_id()), |
| start: offset + text.len(), |
| end: offset + text.len() + name.len(), |
| }]; |
| text.push_str(&name); |
| text.push_str(": "); |
| |
| let ty = ty.make(offset + text.len(), id, scx)?; |
| text.push_str(&ty.text); |
| |
| text.push_str(" = "); |
| let expr = id_to_string(&scx.tcx.hir(), body.hir_id).replace('\n', " "); |
| text.push_str(&expr); |
| |
| text.push(';'); |
| |
| Ok(extend_sig(ty, text, defs, vec![])) |
| } |
| hir::ItemKind::Const(ref ty, ref body) => { |
| let mut text = "const ".to_owned(); |
| let name = self.ident.to_string(); |
| let defs = vec![SigElement { |
| id: id_from_def_id(self.def_id.to_def_id()), |
| start: offset + text.len(), |
| end: offset + text.len() + name.len(), |
| }]; |
| text.push_str(&name); |
| text.push_str(": "); |
| |
| let ty = ty.make(offset + text.len(), id, scx)?; |
| text.push_str(&ty.text); |
| |
| text.push_str(" = "); |
| let expr = id_to_string(&scx.tcx.hir(), body.hir_id).replace('\n', " "); |
| text.push_str(&expr); |
| |
| text.push(';'); |
| |
| Ok(extend_sig(ty, text, defs, vec![])) |
| } |
| hir::ItemKind::Fn(hir::FnSig { ref decl, header, span: _ }, ref generics, _) => { |
| let mut text = String::new(); |
| if let hir::Constness::Const = header.constness { |
| text.push_str("const "); |
| } |
| if hir::IsAsync::Async == header.asyncness { |
| text.push_str("async "); |
| } |
| if let hir::Unsafety::Unsafe = header.unsafety { |
| text.push_str("unsafe "); |
| } |
| text.push_str("fn "); |
| |
| let mut sig = |
| name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?; |
| |
| sig.text.push('('); |
| for i in decl.inputs { |
| // FIXME should descend into patterns to add defs. |
| sig.text.push_str(": "); |
| let nested = i.make(offset + sig.text.len(), Some(i.hir_id), scx)?; |
| sig.text.push_str(&nested.text); |
| sig.text.push(','); |
| sig.defs.extend(nested.defs.into_iter()); |
| sig.refs.extend(nested.refs.into_iter()); |
| } |
| sig.text.push(')'); |
| |
| if let hir::FnRetTy::Return(ref t) = decl.output { |
| sig.text.push_str(" -> "); |
| let nested = t.make(offset + sig.text.len(), None, scx)?; |
| sig.text.push_str(&nested.text); |
| sig.defs.extend(nested.defs.into_iter()); |
| sig.refs.extend(nested.refs.into_iter()); |
| } |
| sig.text.push_str(" {}"); |
| |
| Ok(sig) |
| } |
| hir::ItemKind::Macro(_) => { |
| let mut text = "macro".to_owned(); |
| let name = self.ident.to_string(); |
| text.push_str(&name); |
| text.push_str(&"! {}"); |
| |
| Ok(text_sig(text)) |
| } |
| hir::ItemKind::Mod(ref _mod) => { |
| let mut text = "mod ".to_owned(); |
| let name = self.ident.to_string(); |
| let defs = vec![SigElement { |
| id: id_from_def_id(self.def_id.to_def_id()), |
| start: offset + text.len(), |
| end: offset + text.len() + name.len(), |
| }]; |
| text.push_str(&name); |
| // Could be either `mod foo;` or `mod foo { ... }`, but we'll just pick one. |
| text.push(';'); |
| |
| Ok(Signature { text, defs, refs: vec![] }) |
| } |
| hir::ItemKind::TyAlias(ref ty, ref generics) => { |
| let text = "type ".to_owned(); |
| let mut sig = |
| name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?; |
| |
| sig.text.push_str(" = "); |
| let ty = ty.make(offset + sig.text.len(), id, scx)?; |
| sig.text.push_str(&ty.text); |
| sig.text.push(';'); |
| |
| Ok(merge_sigs(sig.text.clone(), vec![sig, ty])) |
| } |
| hir::ItemKind::Enum(_, ref generics) => { |
| let text = "enum ".to_owned(); |
| let mut sig = |
| name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?; |
| sig.text.push_str(" {}"); |
| Ok(sig) |
| } |
| hir::ItemKind::Struct(_, ref generics) => { |
| let text = "struct ".to_owned(); |
| let mut sig = |
| name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?; |
| sig.text.push_str(" {}"); |
| Ok(sig) |
| } |
| hir::ItemKind::Union(_, ref generics) => { |
| let text = "union ".to_owned(); |
| let mut sig = |
| name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?; |
| sig.text.push_str(" {}"); |
| Ok(sig) |
| } |
| hir::ItemKind::Trait(is_auto, unsafety, ref generics, bounds, _) => { |
| let mut text = String::new(); |
| |
| if is_auto == hir::IsAuto::Yes { |
| text.push_str("auto "); |
| } |
| |
| if let hir::Unsafety::Unsafe = unsafety { |
| text.push_str("unsafe "); |
| } |
| text.push_str("trait "); |
| let mut sig = |
| name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?; |
| |
| if !bounds.is_empty() { |
| sig.text.push_str(": "); |
| sig.text.push_str(&bounds_to_string(bounds)); |
| } |
| // FIXME where clause |
| sig.text.push_str(" {}"); |
| |
| Ok(sig) |
| } |
| hir::ItemKind::TraitAlias(ref generics, bounds) => { |
| let mut text = String::new(); |
| text.push_str("trait "); |
| let mut sig = |
| name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?; |
| |
| if !bounds.is_empty() { |
| sig.text.push_str(" = "); |
| sig.text.push_str(&bounds_to_string(bounds)); |
| } |
| // FIXME where clause |
| sig.text.push(';'); |
| |
| Ok(sig) |
| } |
| hir::ItemKind::Impl(hir::Impl { |
| unsafety, |
| polarity, |
| defaultness, |
| defaultness_span: _, |
| constness, |
| ref generics, |
| ref of_trait, |
| ref self_ty, |
| items: _, |
| }) => { |
| let mut text = String::new(); |
| if let hir::Defaultness::Default { .. } = defaultness { |
| text.push_str("default "); |
| } |
| if let hir::Unsafety::Unsafe = unsafety { |
| text.push_str("unsafe "); |
| } |
| text.push_str("impl"); |
| if let hir::Constness::Const = constness { |
| text.push_str(" const"); |
| } |
| |
| let generics_sig = generics.make(offset + text.len(), id, scx)?; |
| text.push_str(&generics_sig.text); |
| |
| text.push(' '); |
| |
| let trait_sig = if let Some(ref t) = *of_trait { |
| if let hir::ImplPolarity::Negative(_) = polarity { |
| text.push('!'); |
| } |
| let trait_sig = t.path.make(offset + text.len(), id, scx)?; |
| text.push_str(&trait_sig.text); |
| text.push_str(" for "); |
| trait_sig |
| } else { |
| text_sig(String::new()) |
| }; |
| |
| let ty_sig = self_ty.make(offset + text.len(), id, scx)?; |
| text.push_str(&ty_sig.text); |
| |
| text.push_str(" {}"); |
| |
| Ok(merge_sigs(text, vec![generics_sig, trait_sig, ty_sig])) |
| |
| // FIXME where clause |
| } |
| hir::ItemKind::ForeignMod { .. } => Err("extern mod"), |
| hir::ItemKind::GlobalAsm(_) => Err("global asm"), |
| hir::ItemKind::ExternCrate(_) => Err("extern crate"), |
| hir::ItemKind::OpaqueTy(..) => Err("opaque type"), |
| // FIXME should implement this (e.g., pub use). |
| hir::ItemKind::Use(..) => Err("import"), |
| } |
| } |
| } |
| |
| impl<'hir> Sig for hir::Path<'hir> { |
| fn make(&self, offset: usize, id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result { |
| let res = scx.get_path_res(id.ok_or("Missing id for Path")?); |
| |
| let (name, start, end) = match res { |
| Res::PrimTy(..) | Res::SelfTy(..) | Res::Err => { |
| return Ok(Signature { text: path_to_string(self), defs: vec![], refs: vec![] }); |
| } |
| Res::Def(DefKind::AssocConst | DefKind::Variant | DefKind::Ctor(..), _) => { |
| let len = self.segments.len(); |
| if len < 2 { |
| return Err("Bad path"); |
| } |
| // FIXME: really we should descend into the generics here and add SigElements for |
| // them. |
| // FIXME: would be nice to have a def for the first path segment. |
| let seg1 = path_segment_to_string(&self.segments[len - 2]); |
| let seg2 = path_segment_to_string(&self.segments[len - 1]); |
| let start = offset + seg1.len() + 2; |
| (format!("{}::{}", seg1, seg2), start, start + seg2.len()) |
| } |
| _ => { |
| let name = path_segment_to_string(self.segments.last().ok_or("Bad path")?); |
| let end = offset + name.len(); |
| (name, offset, end) |
| } |
| }; |
| |
| let id = id_from_def_id(res.def_id()); |
| Ok(Signature { text: name, defs: vec![], refs: vec![SigElement { id, start, end }] }) |
| } |
| } |
| |
| // This does not cover the where clause, which must be processed separately. |
| impl<'hir> Sig for hir::Generics<'hir> { |
| fn make(&self, offset: usize, _parent_id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result { |
| if self.params.is_empty() { |
| return Ok(text_sig(String::new())); |
| } |
| |
| let mut text = "<".to_owned(); |
| |
| let mut defs = Vec::with_capacity(self.params.len()); |
| for param in self.params { |
| let mut param_text = String::new(); |
| if let hir::GenericParamKind::Const { .. } = param.kind { |
| param_text.push_str("const "); |
| } |
| param_text.push_str(¶m.name.ident().as_str()); |
| defs.push(SigElement { |
| id: id_from_hir_id(param.hir_id, scx), |
| start: offset + text.len(), |
| end: offset + text.len() + param_text.as_str().len(), |
| }); |
| if let hir::GenericParamKind::Const { ref ty, default } = param.kind { |
| param_text.push_str(": "); |
| param_text.push_str(&ty_to_string(&ty)); |
| if let Some(default) = default { |
| param_text.push_str(" = "); |
| param_text.push_str(&id_to_string(&scx.tcx.hir(), default.hir_id)); |
| } |
| } |
| if !param.bounds.is_empty() { |
| param_text.push_str(": "); |
| match param.kind { |
| hir::GenericParamKind::Lifetime { .. } => { |
| let bounds = param |
| .bounds |
| .iter() |
| .map(|bound| match bound { |
| hir::GenericBound::Outlives(lt) => lt.name.ident().to_string(), |
| _ => panic!(), |
| }) |
| .collect::<Vec<_>>() |
| .join(" + "); |
| param_text.push_str(&bounds); |
| // FIXME add lifetime bounds refs. |
| } |
| hir::GenericParamKind::Type { .. } => { |
| param_text.push_str(&bounds_to_string(param.bounds)); |
| // FIXME descend properly into bounds. |
| } |
| hir::GenericParamKind::Const { .. } => { |
| // Const generics cannot contain bounds. |
| } |
| } |
| } |
| text.push_str(¶m_text); |
| text.push(','); |
| } |
| |
| text.push('>'); |
| Ok(Signature { text, defs, refs: vec![] }) |
| } |
| } |
| |
| impl<'hir> Sig for hir::FieldDef<'hir> { |
| fn make(&self, offset: usize, _parent_id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result { |
| let mut text = String::new(); |
| |
| text.push_str(&self.ident.to_string()); |
| let defs = Some(SigElement { |
| id: id_from_hir_id(self.hir_id, scx), |
| start: offset, |
| end: offset + text.len(), |
| }); |
| text.push_str(": "); |
| |
| let mut ty_sig = self.ty.make(offset + text.len(), Some(self.hir_id), scx)?; |
| text.push_str(&ty_sig.text); |
| ty_sig.text = text; |
| ty_sig.defs.extend(defs.into_iter()); |
| Ok(ty_sig) |
| } |
| } |
| |
| impl<'hir> Sig for hir::Variant<'hir> { |
| fn make(&self, offset: usize, parent_id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result { |
| let mut text = self.ident.to_string(); |
| match self.data { |
| hir::VariantData::Struct(fields, r) => { |
| let id = parent_id.ok_or("Missing id for Variant's parent")?; |
| let name_def = SigElement { |
| id: id_from_hir_id(id, scx), |
| start: offset, |
| end: offset + text.len(), |
| }; |
| text.push_str(" { "); |
| let mut defs = vec![name_def]; |
| let mut refs = vec![]; |
| if r { |
| text.push_str("/* parse error */ "); |
| } else { |
| for f in fields { |
| let field_sig = f.make(offset + text.len(), Some(id), scx)?; |
| text.push_str(&field_sig.text); |
| text.push_str(", "); |
| defs.extend(field_sig.defs.into_iter()); |
| refs.extend(field_sig.refs.into_iter()); |
| } |
| } |
| text.push('}'); |
| Ok(Signature { text, defs, refs }) |
| } |
| hir::VariantData::Tuple(fields, id) => { |
| let name_def = SigElement { |
| id: id_from_hir_id(id, scx), |
| start: offset, |
| end: offset + text.len(), |
| }; |
| text.push('('); |
| let mut defs = vec![name_def]; |
| let mut refs = vec![]; |
| for f in fields { |
| let field_sig = f.make(offset + text.len(), Some(id), scx)?; |
| text.push_str(&field_sig.text); |
| text.push_str(", "); |
| defs.extend(field_sig.defs.into_iter()); |
| refs.extend(field_sig.refs.into_iter()); |
| } |
| text.push(')'); |
| Ok(Signature { text, defs, refs }) |
| } |
| hir::VariantData::Unit(id) => { |
| let name_def = SigElement { |
| id: id_from_hir_id(id, scx), |
| start: offset, |
| end: offset + text.len(), |
| }; |
| Ok(Signature { text, defs: vec![name_def], refs: vec![] }) |
| } |
| } |
| } |
| } |
| |
| impl<'hir> Sig for hir::ForeignItem<'hir> { |
| fn make(&self, offset: usize, _parent_id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result { |
| let id = Some(self.hir_id()); |
| match self.kind { |
| hir::ForeignItemKind::Fn(decl, _, ref generics) => { |
| let mut text = String::new(); |
| text.push_str("fn "); |
| |
| let mut sig = |
| name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?; |
| |
| sig.text.push('('); |
| for i in decl.inputs { |
| sig.text.push_str(": "); |
| let nested = i.make(offset + sig.text.len(), Some(i.hir_id), scx)?; |
| sig.text.push_str(&nested.text); |
| sig.text.push(','); |
| sig.defs.extend(nested.defs.into_iter()); |
| sig.refs.extend(nested.refs.into_iter()); |
| } |
| sig.text.push(')'); |
| |
| if let hir::FnRetTy::Return(ref t) = decl.output { |
| sig.text.push_str(" -> "); |
| let nested = t.make(offset + sig.text.len(), None, scx)?; |
| sig.text.push_str(&nested.text); |
| sig.defs.extend(nested.defs.into_iter()); |
| sig.refs.extend(nested.refs.into_iter()); |
| } |
| sig.text.push(';'); |
| |
| Ok(sig) |
| } |
| hir::ForeignItemKind::Static(ref ty, m) => { |
| let mut text = "static ".to_owned(); |
| if m == Mutability::Mut { |
| text.push_str("mut "); |
| } |
| let name = self.ident.to_string(); |
| let defs = vec![SigElement { |
| id: id_from_def_id(self.def_id.to_def_id()), |
| start: offset + text.len(), |
| end: offset + text.len() + name.len(), |
| }]; |
| text.push_str(&name); |
| text.push_str(": "); |
| |
| let ty_sig = ty.make(offset + text.len(), id, scx)?; |
| text.push(';'); |
| |
| Ok(extend_sig(ty_sig, text, defs, vec![])) |
| } |
| hir::ForeignItemKind::Type => { |
| let mut text = "type ".to_owned(); |
| let name = self.ident.to_string(); |
| let defs = vec![SigElement { |
| id: id_from_def_id(self.def_id.to_def_id()), |
| start: offset + text.len(), |
| end: offset + text.len() + name.len(), |
| }]; |
| text.push_str(&name); |
| text.push(';'); |
| |
| Ok(Signature { text, defs, refs: vec![] }) |
| } |
| } |
| } |
| } |
| |
| fn name_and_generics( |
| mut text: String, |
| offset: usize, |
| generics: &hir::Generics<'_>, |
| id: hir::HirId, |
| name: Ident, |
| scx: &SaveContext<'_>, |
| ) -> Result { |
| let name = name.to_string(); |
| let def = SigElement { |
| id: id_from_hir_id(id, scx), |
| start: offset + text.len(), |
| end: offset + text.len() + name.len(), |
| }; |
| text.push_str(&name); |
| let generics: Signature = generics.make(offset + text.len(), Some(id), scx)?; |
| // FIXME where clause |
| let text = format!("{}{}", text, generics.text); |
| Ok(extend_sig(generics, text, vec![def], vec![])) |
| } |
| |
| fn make_assoc_type_signature( |
| id: hir::HirId, |
| ident: Ident, |
| bounds: Option<hir::GenericBounds<'_>>, |
| default: Option<&hir::Ty<'_>>, |
| scx: &SaveContext<'_>, |
| ) -> Result { |
| let mut text = "type ".to_owned(); |
| let name = ident.to_string(); |
| let mut defs = vec![SigElement { |
| id: id_from_hir_id(id, scx), |
| start: text.len(), |
| end: text.len() + name.len(), |
| }]; |
| let mut refs = vec![]; |
| text.push_str(&name); |
| if let Some(bounds) = bounds { |
| text.push_str(": "); |
| // FIXME should descend into bounds |
| text.push_str(&bounds_to_string(bounds)); |
| } |
| if let Some(default) = default { |
| text.push_str(" = "); |
| let ty_sig = default.make(text.len(), Some(id), scx)?; |
| text.push_str(&ty_sig.text); |
| defs.extend(ty_sig.defs.into_iter()); |
| refs.extend(ty_sig.refs.into_iter()); |
| } |
| text.push(';'); |
| Ok(Signature { text, defs, refs }) |
| } |
| |
| fn make_assoc_const_signature( |
| id: hir::HirId, |
| ident: Symbol, |
| ty: &hir::Ty<'_>, |
| default: Option<&hir::Expr<'_>>, |
| scx: &SaveContext<'_>, |
| ) -> Result { |
| let mut text = "const ".to_owned(); |
| let name = ident.to_string(); |
| let mut defs = vec![SigElement { |
| id: id_from_hir_id(id, scx), |
| start: text.len(), |
| end: text.len() + name.len(), |
| }]; |
| let mut refs = vec![]; |
| text.push_str(&name); |
| text.push_str(": "); |
| |
| let ty_sig = ty.make(text.len(), Some(id), scx)?; |
| text.push_str(&ty_sig.text); |
| defs.extend(ty_sig.defs.into_iter()); |
| refs.extend(ty_sig.refs.into_iter()); |
| |
| if let Some(default) = default { |
| text.push_str(" = "); |
| text.push_str(&id_to_string(&scx.tcx.hir(), default.hir_id)); |
| } |
| text.push(';'); |
| Ok(Signature { text, defs, refs }) |
| } |
| |
| fn make_method_signature( |
| id: hir::HirId, |
| ident: Ident, |
| generics: &hir::Generics<'_>, |
| m: &hir::FnSig<'_>, |
| scx: &SaveContext<'_>, |
| ) -> Result { |
| // FIXME code dup with function signature |
| let mut text = String::new(); |
| if let hir::Constness::Const = m.header.constness { |
| text.push_str("const "); |
| } |
| if hir::IsAsync::Async == m.header.asyncness { |
| text.push_str("async "); |
| } |
| if let hir::Unsafety::Unsafe = m.header.unsafety { |
| text.push_str("unsafe "); |
| } |
| text.push_str("fn "); |
| |
| let mut sig = name_and_generics(text, 0, generics, id, ident, scx)?; |
| |
| sig.text.push('('); |
| for i in m.decl.inputs { |
| sig.text.push_str(": "); |
| let nested = i.make(sig.text.len(), Some(i.hir_id), scx)?; |
| sig.text.push_str(&nested.text); |
| sig.text.push(','); |
| sig.defs.extend(nested.defs.into_iter()); |
| sig.refs.extend(nested.refs.into_iter()); |
| } |
| sig.text.push(')'); |
| |
| if let hir::FnRetTy::Return(ref t) = m.decl.output { |
| sig.text.push_str(" -> "); |
| let nested = t.make(sig.text.len(), None, scx)?; |
| sig.text.push_str(&nested.text); |
| sig.defs.extend(nested.defs.into_iter()); |
| sig.refs.extend(nested.refs.into_iter()); |
| } |
| sig.text.push_str(" {}"); |
| |
| Ok(sig) |
| } |