use crate::types; use anyhow::{anyhow, Error, Result}; use clang::Usr; use serde_json::{json, Map as JSONMap, Value as JSONValue}; use thiserror::Error; use std::collections::BTreeMap; use std::convert::{TryFrom, TryInto}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(super) enum EntityKind { Namespace, Variable, Struct, Function, Enum, EnumConstant, TypeAlias, } #[derive(Debug, Error, PartialEq, Eq)] #[error("Unsupported Clang entity kind: {:?}", _0)] pub(super) struct TryFromEntityKindError(String); impl TryFrom for EntityKind { type Error = TryFromEntityKindError; fn try_from(kind: clang::EntityKind) -> Result { Ok(match kind { clang::EntityKind::Namespace | clang::EntityKind::TranslationUnit => { EntityKind::Namespace } clang::EntityKind::VarDecl | clang::EntityKind::FieldDecl => EntityKind::Variable, clang::EntityKind::StructDecl | clang::EntityKind::ClassDecl | clang::EntityKind::ClassTemplate => EntityKind::Struct, clang::EntityKind::FunctionDecl | clang::EntityKind::FunctionTemplate | clang::EntityKind::Method | clang::EntityKind::Constructor | clang::EntityKind::Destructor => EntityKind::Function, clang::EntityKind::TypedefDecl | clang::EntityKind::TypeAliasDecl | clang::EntityKind::TypeAliasTemplateDecl => EntityKind::TypeAlias, clang::EntityKind::EnumDecl => EntityKind::Enum, clang::EntityKind::EnumConstantDecl => EntityKind::EnumConstant, kind => return Err(TryFromEntityKindError(format!("{:?}", kind))), }) } } #[derive(Debug, Clone, PartialEq, Eq)] pub(super) struct Description { name: String, brief: String, detailed: String, } impl<'a> TryFrom> for Description { type Error = Error; fn try_from(entity: clang::Entity) -> Result { let name = entity .get_display_name() .unwrap_or_default(); // TODO: is that the best? if let (Some(brief), Some(comment)) = (entity.get_comment_brief(), entity.get_comment()) { Ok(Description { name, brief, detailed: super::parsing::parse_comment(comment), }) } else { Ok(Description { name, brief: String::new(), detailed: String::new(), }) } } } #[derive(Debug, Clone, PartialEq, Eq)] pub(super) struct Described { pub description: Description, pub entity: T, } type DescribedDynEntity = Described; impl Described { #[allow(clippy::wrong_self_convention)] fn to_dyn_mut(&mut self) -> DescribedRefMut { DescribedRefMut { description: &mut self.description, entity: self.entity.to_dyn_mut(), } } } impl<'a, T> TryFrom> for Described where T: TryFrom, Error = Error>, { type Error = Error; fn try_from(entity: clang::Entity<'a>) -> Result { Ok(Described { description: entity.try_into()?, entity: entity.try_into()?, }) } } impl> From> for types::Entity { fn from(entity: Described) -> Self { let kind = types::EntityKind(entity.entity.kind_name_singular().to_owned()); let partial_entity: PartialEntity = entity.entity.into(); types::Entity { name: entity.description.name, language: types::Language::Clang, kind, brief_description: entity.description.brief, documentation: entity.description.detailed, properties: partial_entity.properties, children: partial_entity.children, } } } #[derive(Debug)] pub(super) struct DescribedRefMut<'d, T> { pub description: &'d mut Description, pub entity: T, } type DescribedDynEntityRefMut<'d, 'e> = DescribedRefMut<'d, DynEntityRefMut<'e>>; impl<'d, 'e, T: Entity> DescribedRefMut<'d, &'e mut T> { fn from_dyn_mut(dyn_entity: DescribedDynEntityRefMut<'d, 'e>) -> Option { Some(DescribedRefMut { description: dyn_entity.description, entity: Entity::from_dyn_mut(dyn_entity.entity)?, }) } } pub(super) trait Entity: Traversable + KindName { const KIND: EntityKind; fn from_dyn_mut(dyn_entity: DynEntityRefMut) -> Option<&mut Self>; fn into_dyn(self) -> DynEntity; fn to_dyn_mut(&mut self) -> DynEntityRefMut; } pub(super) trait Traversable { fn has_child_from_kind(&self, usr: &Usr, kind: EntityKind) -> bool; fn get_mut_child_from_kind( &mut self, usr: &Usr, kind: EntityKind, ) -> Option; fn insert(&mut self, usr: Usr, child: DescribedDynEntity) -> Result<()>; } pub(super) trait KindName { fn kind_name_singular(&self) -> &'static str; fn kind_name_plural(&self) -> &'static str; } #[derive(Debug)] pub(super) struct PartialEntity { properties: JSONMap, children: types::Children, } fn entity_id(usr: Usr) -> types::EntityId { fn is_identifier_char(c: char) -> bool { c.is_ascii() || c == '-' || c == '_' || c == ':' || c == '.' } // This is a somewhat ugly workaround, because Pandoc parses markdown identifiers as alphanum // or one of "-_:.", which means Pandoc can't parse libclang's USRs in title ids and related. // // types::EntityId( usr.0 .replace("@", "::") .replace("#", ".") .replace("$", "-") .replace(|c| !is_identifier_char(c), ""), ) } macro_rules! entity { ( $(#[$meta: meta])* $struct_vis: vis struct $name: ident { $($( $(#[$field_meta: meta])* $field_vis: vis $field: ident : $type: ty, )+)? $(#children => [ $($child_name: ident: $child_type: ident),* $(,)? ],)? $(#properties($properties_var: ident) => $properties: expr,)? } ) => { #[derive(Debug, Clone, PartialEq, Eq)] $(#[$meta])* $struct_vis struct $name { $($( $(#[$field_meta])* $field_vis $field: $type, )+)? $($($child_name: BTreeMap>),*)? } impl Entity for $name { const KIND: EntityKind = EntityKind::$name; fn from_dyn_mut(dyn_entity: DynEntityRefMut) -> Option<&mut Self> { match dyn_entity { DynEntityRefMut::$name(me) => Some(me), _ => None, } } fn into_dyn(self) -> DynEntity { DynEntity::$name(self) } fn to_dyn_mut(&mut self) -> DynEntityRefMut { DynEntityRefMut::$name(self) } } impl Traversable for $name { #[allow(unused_variables, unreachable_patterns)] fn has_child_from_kind(&self, usr: &Usr, kind: EntityKind) -> bool { match kind { $($( EntityKind::$child_type => self.$child_name.contains_key(usr), )*)? _ => false, } } #[allow(unused_variables, unreachable_patterns)] fn get_mut_child_from_kind( &mut self, usr: &Usr, kind: EntityKind ) -> Option { match kind { $($( EntityKind::$child_type => { self.$child_name.get_mut(usr).map(Described::to_dyn_mut) } )*)? _ => None, } } #[allow(unused_variables, unreachable_patterns)] fn insert(&mut self, usr: Usr, child: DescribedDynEntity) -> Result<()> { match child.entity { $($( DynEntity::$child_type(entity) => { self.$child_name.insert( usr, Described { description: child.description, entity } ); Ok(()) } )*)? // TODO: Properly define an error other => Err(anyhow!("Impossible to insert {:?} into {:?}", other, Self::KIND)), } } } // TODO: explain why we do this (hygienic) entity!{@partial_entity $name, $($properties_var,)? $($($child_name)*)?, $($properties)?} }; (@partial_entity $name: ident, $entity_var: ident, $($child_name: ident)*, $($properties: expr)?) => { impl<'a> From<$name> for PartialEntity { #[allow(unused_variables, unused_mut)] fn from($entity_var: $name) -> Self { let mut children = types::Children::default(); $( for (usr, child) in $entity_var.$child_name { children.entry(types::ChildrenKind(String::from( child.entity.kind_name_plural(), ))) .or_default() .insert(entity_id(usr), child.into()); } )* let properties = entity!(@properties $($properties)?); Self { properties, children } } } }; (@partial_entity $name: ident, $($child_name: ident)*, $($properties: expr)?) => { entity!{@partial_entity $name, entity, $($child_name)*, $($properties)?} }; (@properties $properties: expr) => {{ match $properties { JSONValue::Object(map) => map, _ => panic!("Entity properties not given in JSON object form"), } }}; (@properties) => { Default::default() } } #[derive(Debug, Clone, PartialEq, Eq)] pub(super) struct TopLevel(Described); impl Default for TopLevel { fn default() -> Self { TopLevel(Described:: { description: Description { name: String::new(), brief: String::new(), detailed: String::new(), }, entity: Namespace::new_global(), }) } } impl From for types::Entity { fn from(toplevel: TopLevel) -> Self { toplevel.0.into() } } #[derive(Debug)] pub(super) enum EntityEntry<'d, 'e, T> { Vacant { parent: DescribedRefMut<'d, &'e mut Namespace>, }, Occupied { entity: DescribedRefMut<'d, T>, }, } #[derive(Debug, Error, PartialEq, Eq)] pub(super) enum TopLevelEntryError { #[error("Entity has no parent")] NoParent, #[error("Entity has no USR")] NoUsr, #[error("Entity kind is not supported")] UnknownKind, } impl TopLevel { pub(super) fn get_dyn_mut(&mut self, path: clang::Entity) -> Option { if path.get_kind() == clang::EntityKind::TranslationUnit { return Some(self.0.to_dyn_mut()); } let usr = path.get_usr()?; let parent_path = path .get_semantic_parent() .expect("get_semantic_parent() returned None"); let kind = path.get_kind().try_into().ok()?; self.get_dyn_mut(parent_path).and_then(|parent| { parent .entity .map(|parent| parent.get_mut_child_from_kind(&usr, kind)) }) } // TODO: does not check whether a vacant entry is allowed to be in parent. // For example, it can return a vacant EnumConstant inside a Struct. pub(super) fn entry_dyn( &mut self, path: clang::Entity, ) -> Result, TopLevelEntryError> { let kind = path .get_kind() .try_into() .map_err(|_err| TopLevelEntryError::UnknownKind)?; let usr = path.get_usr().ok_or(TopLevelEntryError::NoUsr)?; let parent_path = match path.get_semantic_parent() { Some(parent) => parent, // No parent? It must be the TranslationUnit / Global Namespace None => { return Ok(EntityEntry::Vacant { parent: DescribedRefMut { description: &mut self.0.description, entity: &mut self.0.entity, }, }) } }; let dyn_parent = self .get_dyn_mut(parent_path) .ok_or(TopLevelEntryError::NoParent)?; if dyn_parent .entity .map_ref(|parent| !parent.has_child_from_kind(&usr, kind)) { return Ok(EntityEntry::Vacant { parent: DescribedRefMut::from_dyn_mut(dyn_parent).unwrap(), }); } match dyn_parent .entity .map(|parent| parent.get_mut_child_from_kind(&usr, kind)) { Some(entity) => Ok(EntityEntry::Occupied { entity }), None => unreachable!(), } } } // TODO: all of the DynEntity business can probably be macro generated too #[derive(Debug)] pub(super) enum DynEntity { Namespace(Namespace), Variable(Variable), Struct(Struct), Function(Function), Enum(Enum), EnumConstant(EnumConstant), TypeAlias(TypeAlias), } #[derive(Debug)] pub(super) enum DynEntityRefMut<'a> { Namespace(&'a mut Namespace), Variable(&'a mut Variable), Struct(&'a mut Struct), Function(&'a mut Function), Enum(&'a mut Enum), EnumConstant(&'a mut EnumConstant), TypeAlias(&'a mut TypeAlias), } impl<'a> DynEntityRefMut<'a> { fn map(self, f: F) -> U where F: FnOnce(&'a mut dyn Traversable) -> U, { match self { DynEntityRefMut::Namespace(namespace) => f(namespace), DynEntityRefMut::Variable(variable) => f(variable), DynEntityRefMut::Struct(r#struct) => f(r#struct), DynEntityRefMut::Function(function) => f(function), DynEntityRefMut::Enum(r#enum) => f(r#enum), DynEntityRefMut::EnumConstant(enum_constant) => f(enum_constant), DynEntityRefMut::TypeAlias(type_alias) => f(type_alias), } } fn map_ref(&self, f: F) -> U where F: FnOnce(&dyn Traversable) -> U, { match self { DynEntityRefMut::Namespace(namespace) => f(*namespace), DynEntityRefMut::Variable(variable) => f(*variable), DynEntityRefMut::Struct(r#struct) => f(*r#struct), DynEntityRefMut::Function(function) => f(*function), DynEntityRefMut::Enum(r#enum) => f(*r#enum), DynEntityRefMut::EnumConstant(enum_constant) => f(*enum_constant), DynEntityRefMut::TypeAlias(type_alias) => f(*type_alias), } } } impl<'a> TryFrom> for DynEntity { type Error = Error; fn try_from(entity: clang::Entity) -> Result { Ok(match entity.get_kind().try_into()? { EntityKind::Namespace => Namespace::try_from(entity)?.into_dyn(), EntityKind::Variable => Variable::try_from(entity)?.into_dyn(), EntityKind::Struct => Struct::try_from(entity)?.into_dyn(), EntityKind::Function => Function::try_from(entity)?.into_dyn(), EntityKind::Enum => Enum::try_from(entity)?.into_dyn(), EntityKind::EnumConstant => EnumConstant::try_from(entity)?.into_dyn(), EntityKind::TypeAlias => TypeAlias::try_from(entity)?.into_dyn(), }) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] struct ClassMemberProperties { accessibility: clang::Accessibility, } #[derive(Debug, Error, PartialEq, Eq)] pub(super) enum TryFromClassMemberPropsError { #[error("Failed to get accessibility status")] Accessibility, } impl ClassMemberProperties { fn try_from_clang(entity: clang::Entity) -> Result { Ok(ClassMemberProperties { accessibility: entity .get_accessibility() .ok_or(TryFromClassMemberPropsError::Accessibility)?, }) } } impl From for JSONMap { fn from(member_properties: ClassMemberProperties) -> JSONMap { let mut properties = JSONMap::new(); properties.insert( String::from("accessibility"), match member_properties.accessibility { clang::Accessibility::Private => JSONValue::String(String::from("private")), clang::Accessibility::Protected => JSONValue::String(String::from("protected")), clang::Accessibility::Public => JSONValue::String(String::from("public")), }, ); properties } } // ================= // === Namespace === // ================= entity! { pub(super) struct Namespace { /// Whether this is the global namespace global: bool, #children => [ member_namespaces: Namespace, member_variables: Variable, member_structs: Struct, member_functions: Function, member_type_aliases: TypeAlias, member_enums: Enum, ], #properties(entity) => json!({"global": entity.global}), } } impl Namespace { fn new_global() -> Self { Namespace { global: true, ..Default::default() } } } impl KindName for Namespace { fn kind_name_singular(&self) -> &'static str { "namespace" } fn kind_name_plural(&self) -> &'static str { "namespaces" } } impl Default for Namespace { fn default() -> Self { Namespace { global: false, member_variables: Default::default(), member_structs: Default::default(), member_namespaces: Default::default(), member_functions: Default::default(), member_type_aliases: Default::default(), member_enums: Default::default(), } } } impl<'a> TryFrom> for Namespace { type Error = Error; fn try_from(entity: clang::Entity) -> Result { assert_eq!( entity.get_kind().try_into(), Ok(EntityKind::Namespace), "Trying to parse a non-variable into a variable" ); debug!("Parsing Namespace: {:?}", entity); // Do not recurse here, but recurse in the main loop, since namespace // definitions is allowed to change between translation units Ok(Namespace::default()) } } // ================ // === Variable === // ================ #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum VariableKind { Variable, Field { member_properties: ClassMemberProperties, }, } entity! { pub(super) struct Variable { kind: VariableKind, r#type: String, #properties(entity) => { let mut properties = json!({"type": entity.r#type}); if let VariableKind::Field { member_properties } = entity.kind { properties.as_object_mut().unwrap().extend(JSONMap::from(member_properties)); } properties }, } } impl KindName for Variable { fn kind_name_singular(&self) -> &'static str { match self.kind { VariableKind::Variable => "variable", VariableKind::Field { .. } => "field", } } fn kind_name_plural(&self) -> &'static str { match self.kind { VariableKind::Variable => "variables", VariableKind::Field { .. } => "fields", } } } impl<'a> TryFrom> for Variable { type Error = Error; fn try_from(entity: clang::Entity) -> Result { let clang_kind = entity.get_kind(); assert_eq!( clang_kind.try_into(), Ok(EntityKind::Variable), "Trying to parse a non-variable into a variable" ); debug!("Parsing Variable: {:?}", entity); let kind = match clang_kind { clang::EntityKind::VarDecl => VariableKind::Variable, clang::EntityKind::FieldDecl => VariableKind::Field { // TODO: should probably not return on error member_properties: ClassMemberProperties::try_from_clang(entity)?, }, // We panic here because we've asserted the type in the beginning other => panic!("Trying to parse {:?} as a variable", other), }; let r#type = entity.get_type().unwrap().get_display_name(); trace!("Variable has type: {:?}", r#type); Ok(Variable { kind, r#type }) } } // ============== // === Struct === // ============== #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(super) enum StructKind { Struct, Class, } entity! { pub(super) struct Struct { kind: StructKind, #children => [ member_variables: Variable, member_structs: Struct, member_functions: Function, member_enums: Enum, member_type_aliases: TypeAlias, ], } } impl KindName for Struct { fn kind_name_singular(&self) -> &'static str { match self.kind { StructKind::Struct => "struct", StructKind::Class => "class", } } fn kind_name_plural(&self) -> &'static str { match self.kind { StructKind::Struct => "structs", StructKind::Class => "classes", } } } impl<'a> TryFrom> for Struct { type Error = Error; fn try_from(entity: clang::Entity) -> Result { let clang_kind = entity.get_kind(); assert_eq!( clang_kind.try_into(), Ok(EntityKind::Struct), "Trying to parse a non-struct into a struct" ); debug!("Parsing Struct: {:?}", entity); let mut member_variables = BTreeMap::new(); let mut member_structs = BTreeMap::new(); let mut member_functions = BTreeMap::new(); let mut member_enums = BTreeMap::new(); let mut member_type_aliases = BTreeMap::new(); for child in entity.get_children() { trace!("Struct has child: {:?}", child); let kind = child.get_kind(); match kind { ::clang::EntityKind::AccessSpecifier | ::clang::EntityKind::BaseSpecifier => { continue } _ => {} } let mut parse_child = || -> Result<()> { match kind.try_into() { Ok(EntityKind::Variable) => { let child_usr = child .get_usr() .ok_or_else(|| anyhow!("no usr for: {:?}", child))?; member_variables.insert(child_usr, Described::::try_from(child)?); } Ok(EntityKind::Struct) => { let child_usr: Usr = child .get_usr() .ok_or_else(|| anyhow!("no usr for: {:?}", child))?; member_structs.insert(child_usr, Described::::try_from(child)?); } Ok(EntityKind::Function) => { let child_usr = child .get_usr() .ok_or_else(|| anyhow!("no usr for: {:?}", child))?; member_functions.insert(child_usr, Described::::try_from(child)?); } Ok(EntityKind::Enum) => { let child_usr = child .get_usr() .ok_or_else(|| anyhow!("no usr for: {:?}", child))?; member_enums.insert(child_usr, Described::::try_from(child)?); } Ok(EntityKind::TypeAlias) => { let child_usr = child .get_usr() .ok_or_else(|| anyhow!("no usr for: {:?}", child))?; member_type_aliases .insert(child_usr, Described::::try_from(child)?); } Ok(other) => warn!("Unsupported child of struct {:?}: {:?}", other, child), Err(err) => info!("Error while parsing entity {:?}: {}", child, err), } Ok(()) }; match parse_child() { Ok(()) => {} Err(err) => { warn!("Error while parsing child {:?}: {}", child, err); } } } let kind = match clang_kind { clang::EntityKind::StructDecl => StructKind::Struct, clang::EntityKind::ClassDecl | clang::EntityKind::ClassTemplate => StructKind::Class, // We panic here because we've asserted the type in the beginning other => panic!("Trying to parse {:?} as a variable", other), }; Ok(Struct { kind, member_variables, member_structs, member_functions, member_enums, member_type_aliases, }) } } // ================ // === Function === // ================ #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(super) enum FunctionKind { Function, Method, } entity! { pub(super) struct Function { kind: FunctionKind, arguments: Vec, return_type: String, } } #[derive(Debug, Clone, PartialEq, Eq)] pub(super) struct FunctionArgument { pub(super) name: String, pub(super) r#type: String, } impl KindName for Function { fn kind_name_singular(&self) -> &'static str { match self.kind { FunctionKind::Function => "function", FunctionKind::Method => "method", } } fn kind_name_plural(&self) -> &'static str { match self.kind { FunctionKind::Function => "functions", FunctionKind::Method => "methods", } } } impl<'a> TryFrom> for Function { type Error = Error; fn try_from(entity: clang::Entity) -> Result { let clang_kind = entity.get_kind(); assert_eq!( clang_kind.try_into(), Ok(EntityKind::Function), "Trying to parse a non-function into a function" ); debug!("Parsing Function: {:?}", entity); let return_type = entity.get_result_type().unwrap().get_display_name(); trace!("Function has return type: {:?}", return_type); let arguments = entity .get_arguments() // TODO: this seems weird, but it fixes a None for a function that takes only a // variadic argument from its own template declaration. .unwrap_or_else(|| vec![]) .into_iter() .map(|arg| { let name = arg .get_display_name() .unwrap_or_else(|| String::from("unnamed")); let r#type = arg.get_type().unwrap().get_display_name(); trace!("Function has argument {:?} of type {:?}", name, r#type); FunctionArgument { name, r#type } }) .collect(); let kind = match clang_kind { clang::EntityKind::FunctionDecl | clang::EntityKind::FunctionTemplate => { FunctionKind::Function } clang::EntityKind::Method | clang::EntityKind::Constructor | clang::EntityKind::Destructor => FunctionKind::Method, // We panic here because we've asserted the type in the beginning other => panic!("Trying to parse {:?} as a function", other), }; Ok(Function { kind, arguments, return_type, }) } } // ============ // === Enum === // ============ entity! { pub(super) struct Enum { underlying_type: String, #children => [ constants: EnumConstant, ], } } impl KindName for Enum { fn kind_name_singular(&self) -> &'static str { "enum" } fn kind_name_plural(&self) -> &'static str { "enums" } } impl<'a> TryFrom> for Enum { type Error = Error; fn try_from(entity: clang::Entity) -> Result { assert_eq!( entity.get_kind().try_into(), Ok(EntityKind::Enum), "Trying to parse a non-type alias into a type alias" ); debug!("Parsing enum: {:?}", entity); let underlying_type = entity .get_enum_underlying_type() .ok_or_else(|| anyhow!("No enum underlying type"))? .get_display_name(); let mut constants = BTreeMap::new(); for child in entity.get_children() { trace!("Enum has child: {:?}", child); match child.get_kind().try_into() { Ok(EntityKind::EnumConstant) => { let child_usr = child .get_usr() .ok_or_else(|| anyhow!("no usr for: {:?}", child))?; constants.insert(child_usr, Described::::try_from(child)?); } Ok(other) => warn!("Unsupported child of enum {:?}: {:?}", other, child), Err(err) => info!("Error while parsing entity {:?}: {}", child, err), } } Ok(Enum { underlying_type, constants, }) } } // ====================== // === Enum Constants === // ====================== entity! { pub(super) struct EnumConstant { value: String, } } impl KindName for EnumConstant { fn kind_name_singular(&self) -> &'static str { "enum variant" } fn kind_name_plural(&self) -> &'static str { "enum variants" } } impl<'a> TryFrom> for EnumConstant { type Error = Error; fn try_from(entity: clang::Entity) -> Result { assert_eq!( entity.get_kind().try_into(), Ok(EntityKind::EnumConstant), "Trying to parse a non-type alias into a type alias" ); debug!("Parsing enum: {:?}", entity); let (signed_value, unsigned_value) = entity .get_enum_constant_value() .ok_or_else(|| anyhow!("No enum constant value"))?; let is_signed = entity .get_semantic_parent() .ok_or_else(|| anyhow!("Enum constant not attached to an enum"))? .get_enum_underlying_type() .ok_or_else(|| anyhow!("Enum doesn't have an underlying type"))? .get_canonical_type() .is_signed_integer(); let value = if is_signed { format!("{}", signed_value) } else { format!("{}", unsigned_value) }; Ok(EnumConstant { value }) } } // ================== // === Type Alias === // ================== entity! { pub(super) struct TypeAlias { referee: String, } } impl KindName for TypeAlias { fn kind_name_singular(&self) -> &'static str { "type alias" } fn kind_name_plural(&self) -> &'static str { "type aliases" } } impl<'a> TryFrom> for TypeAlias { type Error = Error; fn try_from(entity: clang::Entity) -> Result { assert_eq!( entity.get_kind().try_into(), Ok(EntityKind::TypeAlias), "Trying to parse a non-type alias into a type alias" ); debug!("Parsing typedef: {:?}", entity); let referee = entity .get_typedef_underlying_type() .ok_or_else(|| anyhow!("No underlying type"))? .get_display_name(); Ok(TypeAlias { referee }) } }