From 5b137e8bb767aa2b381e9d5a0710583c4edaa889 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Sun, 23 Feb 2020 17:01:14 +0100 Subject: Big clang refactoring - Manipulating the entity tree should be much nicer: - "Typesafe type erasure" - Entry API for the tree - *NOT clang specific*: There is now a single entity at the top of the tree: the global namespace scope for the case of C/C++ --- src/parser/clang/entities.rs | 1477 +++++++++++++++++++++++++++++++----------- 1 file changed, 1106 insertions(+), 371 deletions(-) (limited to 'src/parser/clang/entities.rs') diff --git a/src/parser/clang/entities.rs b/src/parser/clang/entities.rs index 0e9305b..b1689aa 100644 --- a/src/parser/clang/entities.rs +++ b/src/parser/clang/entities.rs @@ -1,541 +1,1276 @@ -use crate::types::{self, *}; +use crate::types; -use clang::{EntityKind, Usr}; -use serde_json::{Map, Value as JSONValue}; +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; - -// TODO: factor out hardcoded strings +use std::convert::{TryFrom, TryInto}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(super) enum ClangEntityKind { +pub(super) enum EntityKind { Namespace, - Variable(VariableKind), - Struct(StructKind), - Function(FunctionKind), - Typedef, - Enum, - EnumConstant, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(super) enum VariableKind { Variable, - Field, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(super) enum StructKind { Struct, - Class, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(super) enum FunctionKind { Function, - Method, -} - -impl ClangEntityKind { - fn to_str_singular(self) -> &'static str { - match self { - ClangEntityKind::Namespace => "namespace", - ClangEntityKind::Variable(VariableKind::Variable) => "variable", - ClangEntityKind::Variable(VariableKind::Field) => "field", - ClangEntityKind::Struct(StructKind::Struct) => "struct", - ClangEntityKind::Struct(StructKind::Class) => "class", - ClangEntityKind::Function(FunctionKind::Function) => "function", - ClangEntityKind::Function(FunctionKind::Method) => "method", - ClangEntityKind::Typedef => "typedef", - ClangEntityKind::Enum => "enum", - ClangEntityKind::EnumConstant => "enum-constant", - } - } - - fn to_str_plural(self) -> &'static str { - match self { - ClangEntityKind::Namespace => "namespaces", - ClangEntityKind::Variable(VariableKind::Variable) => "variables", - ClangEntityKind::Variable(VariableKind::Field) => "fields", - ClangEntityKind::Struct(StructKind::Struct) => "structs", - ClangEntityKind::Struct(StructKind::Class) => "classes", - ClangEntityKind::Function(FunctionKind::Function) => "functions", - ClangEntityKind::Function(FunctionKind::Method) => "methods", - ClangEntityKind::Typedef => "typedefs", - ClangEntityKind::Enum => "enums", - ClangEntityKind::EnumConstant => "enum-constants", - } - } + Enum, + EnumConstant, + TypeAlias, } -#[derive(Debug, Error)] +#[derive(Debug, Error, PartialEq, Eq)] #[error("Unsupported Clang entity kind: {:?}", _0)] pub(super) struct TryFromEntityKindError(String); -impl TryFrom for ClangEntityKind { +impl TryFrom for EntityKind { type Error = TryFromEntityKindError; - fn try_from(kind: EntityKind) -> Result { + fn try_from(kind: clang::EntityKind) -> Result { Ok(match kind { - EntityKind::Namespace => ClangEntityKind::Namespace, - EntityKind::VarDecl => ClangEntityKind::Variable(VariableKind::Variable), - EntityKind::FieldDecl => ClangEntityKind::Variable(VariableKind::Field), - EntityKind::FunctionDecl | EntityKind::FunctionTemplate => { - ClangEntityKind::Function(FunctionKind::Function) - } - EntityKind::Method | EntityKind::Constructor | EntityKind::Destructor => { - ClangEntityKind::Function(FunctionKind::Method) + clang::EntityKind::Namespace | clang::EntityKind::TranslationUnit => { + EntityKind::Namespace } - EntityKind::StructDecl => ClangEntityKind::Struct(StructKind::Struct), - EntityKind::ClassDecl | EntityKind::ClassTemplate => { - ClangEntityKind::Struct(StructKind::Class) - } - EntityKind::TypedefDecl - | EntityKind::TypeAliasDecl - | EntityKind::TypeAliasTemplateDecl => ClangEntityKind::Typedef, - EntityKind::EnumDecl => ClangEntityKind::Enum, - EntityKind::EnumConstantDecl => ClangEntityKind::EnumConstant, + 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)] -pub(super) struct PartialEntity { - properties: Map, - children: Children, +#[derive(Debug, Clone, PartialEq, Eq)] +pub(super) struct Description { + name: String, + brief: String, + detailed: String, } -pub(super) trait ClangEntity { - fn kind(&self) -> ClangEntityKind; - - fn get_member_namespaces(&mut self) -> Option<&mut BTreeMap>> { - None +impl<'a> TryFrom> for Description { + type Error = Error; + + fn try_from(entity: clang::Entity) -> Result { + let name = entity + .get_display_name() + .ok_or_else(|| anyhow!("Entity has no name: {:?}", entity))?; + + // 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(), + }) + } } - fn get_member_variables(&mut self) -> Option<&mut BTreeMap>> { - None +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(super) struct Described { + pub description: Description, + pub entity: T, +} + +type DescribedDynEntity = Described; + +impl Described { + /* + fn into_dyn(self) -> Described { + Described { + description: self.description, + entity: self.entity.into_dyn(), + } } - fn get_member_structs(&mut self) -> Option<&mut BTreeMap>> { - None + */ + + fn to_dyn(&self) -> DescribedRef { + DescribedRef { + description: &self.description, + entity: self.entity.to_dyn(), + } } - fn get_member_functions(&mut self) -> Option<&mut BTreeMap>> { - None + + #[allow(clippy::wrong_self_convention)] + fn to_dyn_mut(&mut self) -> DescribedRefMut { + DescribedRefMut { + description: &mut self.description, + entity: self.entity.to_dyn_mut(), + } } - fn get_member_typedefs(&mut self) -> Option<&mut BTreeMap>> { - None +} + +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: T::try_from(entity)?, + }) } - fn get_member_enums(&mut self) -> Option<&mut BTreeMap>> { - None +} + +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, Clone)] +pub(super) struct DescribedRef<'a, T> { + pub description: &'a Description, + pub entity: T, +} + +type DescribedDynEntityRef<'d, 'e> = DescribedRef<'d, DynEntityRef<'e>>; + /* -pub(super) trait FromNamespaceParent: ClangEntity + Sized { - fn from_namespace_parent<'a>( - parent: &'a mut Described, - name: &Usr, - ) -> Option<&'a mut Described>; +impl<'d, 'e, T: Entity> DescribedRef<'d, &'e T> { + fn from_dyn_ref(dyn_entity: DescribedDynEntityRef<'d, 'e>) -> Option { + Some(DescribedRef { + description: dyn_entity.description, + entity: Entity::from_dyn_ref(dyn_entity.entity)?, + }) + } } */ -pub(super) trait NamespaceParentManipulation { - fn get_members_mut(&mut self) -> Option<&mut BTreeMap>>; +#[derive(Debug, Clone)] +pub(super) struct DescribedRefMut<'a, T> { + pub description: &'a Description, + pub entity: T, } -impl NamespaceParentManipulation for U -where - U: ClangEntity, -{ - fn get_members_mut(&mut self) -> Option<&mut BTreeMap>> { - self.get_member_namespaces() +type DescribedDynEntityRefMut<'a, 'b> = DescribedRefMut<'a, DynEntityRefMut<'b>>; + +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)?, + }) } } -impl NamespaceParentManipulation for U -where - U: ClangEntity, -{ - fn get_members_mut(&mut self) -> Option<&mut BTreeMap>> { - self.get_member_variables() +pub(super) trait Entity: Traversable + KindName { + const KIND: EntityKind; + + /* + /// Transform a generic entity to a specific entity + fn try_into_specific(self) -> Option + where + Self: Sized, + { + T::from_dyn(self.into_dyn()) } -} -impl NamespaceParentManipulation for U -where - U: ClangEntity, -{ - fn get_members_mut(&mut self) -> Option<&mut BTreeMap>> { - self.get_member_functions() + /// Transform a generic entity to a specific entity + /// + /// Panics if not the right entity kind + fn into_specific(self) -> T + where + Self: Sized, + { + self.try_into_specific().unwrap() } + */ + + fn from_dyn(dyn_entity: DynEntity) -> Option + where + Self: Sized; + fn from_dyn_ref(dyn_entity: DynEntityRef) -> Option<&Self>; + fn from_dyn_mut(dyn_entity: DynEntityRefMut) -> Option<&mut Self>; + fn into_dyn(self) -> DynEntity; + fn to_dyn(&self) -> DynEntityRef; + fn to_dyn_mut(&mut self) -> DynEntityRefMut; } -impl NamespaceParentManipulation for U -where - U: ClangEntity, -{ - fn get_members_mut(&mut self) -> Option<&mut BTreeMap>> { - self.get_member_structs() - } +pub(super) trait Traversable { + fn has_child_from_kind(&self, usr: &Usr, kind: EntityKind) -> bool; + fn get_child_from_kind(&self, usr: &Usr, kind: EntityKind) -> Option; + fn get_mut_child_from_kind( + &mut self, + usr: &Usr, + kind: EntityKind, + ) -> Option; + fn insert(&mut self, usr: Usr, child: DescribedDynEntity) -> Result<()>; } -impl NamespaceParentManipulation for U -where - U: ClangEntity, -{ - fn get_members_mut(&mut self) -> Option<&mut BTreeMap>> { - self.get_member_typedefs() - } +pub(super) trait KindName { + fn kind_name_singular(&self) -> &'static str; + fn kind_name_plural(&self) -> &'static str; } -impl NamespaceParentManipulation for U -where - U: ClangEntity, -{ - fn get_members_mut(&mut self) -> Option<&mut BTreeMap>> { - self.get_member_enums() - } +#[derive(Debug)] +pub(super) struct PartialEntity { + properties: JSONMap, + children: types::Children, } -fn entity_id(usr: Usr) -> EntityId { +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. // // - EntityId(usr.0.replace("@", "::").replace("#", ".").replace("$", "-")) + types::EntityId( + usr.0 + .replace("@", "::") + .replace("#", ".") + .replace("$", "-") + .replace(|c| !is_identifier_char(c), ""), + ) } -fn append_children>( - acc: &mut types::Children, - to_insert: BTreeMap>, -) { - for (usr, child) in to_insert { - acc.entry(ChildrenKind(String::from( - child.entity.kind().to_str_plural(), - ))) - .or_default() - .insert(entity_id(usr), child.into()); - } -} +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> From> for Entity { - fn from(entity: Described) -> Self { - let kind = EntityKind(String::from(entity.entity.kind().to_str_singular())); - let partial_entity: PartialEntity = entity.entity.into(); + impl Entity for $name { + const KIND: EntityKind = EntityKind::$name; - Entity { - name: entity.description.name, - language: Language::Clang, - kind, - brief_description: entity.description.brief, - documentation: entity.description.detailed, - properties: partial_entity.properties, - children: partial_entity.children, + fn from_dyn(dyn_entity: DynEntity) -> Option { + match dyn_entity { + DynEntity::$name(me) => Some(me), + _ => None, + } + } + + fn from_dyn_ref(dyn_entity: DynEntityRef) -> Option<&Self> { + match dyn_entity { + DynEntityRef::$name(me) => Some(me), + _ => None, + } + } + + 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(&self) -> DynEntityRef { + DynEntityRef::$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_child_from_kind( + &self, + usr: &Usr, + kind: EntityKind + ) -> Option { + match kind { + $($( + EntityKind::$child_type => { + self.$child_name.get(usr).map(Described::to_dyn) + } + )*)? + _ => None, + } + } + + #[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, Default)] -pub(super) struct Description { - pub(super) name: String, - pub(super) brief: String, - pub(super) detailed: String, +#[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(), + }) + } } -#[derive(Debug, Clone)] -pub(super) struct Described { - pub(super) description: Description, - pub(super) entity: T, +impl From for types::Entity { + fn from(toplevel: TopLevel) -> Self { + toplevel.0.into() + } } -#[derive(Debug, Clone)] -pub(super) struct Namespace { - pub(super) member_namespaces: BTreeMap>, - pub(super) member_variables: BTreeMap>, - pub(super) member_structs: BTreeMap>, - pub(super) member_functions: BTreeMap>, - pub(super) member_typedefs: BTreeMap>, - pub(super) member_enums: BTreeMap>, +// TODO: Probably remove NoParent in favor of the Error variant of Result +#[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 ClangEntity for Namespace { - fn kind(&self) -> ClangEntityKind { - ClangEntityKind::Namespace +impl TopLevel { + /* + pub(super) fn get_dyn(&self, path: clang::Entity) -> Option { + if path.get_kind() == clang::EntityKind::TranslationUnit { + return Some(self.0.to_dyn()); + } + 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(parent_path).and_then(|parent| { + parent + .entity + .map(|parent| parent.get_child_from_kind(&usr, kind)) + }) + } + */ + + 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)) + }) } - fn get_member_namespaces(&mut self) -> Option<&mut BTreeMap>> { - Some(&mut self.member_namespaces) + /* + pub(super) fn get(&self, path: clang::Entity) -> Option> + where + T: Entity, + { + assert_eq!(path.get_kind().try_into(), Ok(T::KIND),); + self.get_dyn(path).and_then(DescribedRef::from_dyn_ref) } - fn get_member_variables(&mut self) -> Option<&mut BTreeMap>> { - Some(&mut self.member_variables) + + pub(super) fn get_mut(&mut self, path: clang::Entity) -> Option> + where + T: Entity, + { + assert_eq!(path.get_kind().try_into(), Ok(T::KIND),); + self.get_dyn_mut(path) + .and_then(DescribedRefMut::from_dyn_mut) } - fn get_member_structs(&mut self) -> Option<&mut BTreeMap>> { - Some(&mut self.member_structs) + + pub(super) fn insert_dyn( + &mut self, + path: clang::Entity, + entity: DescribedDynEntity, + ) -> Result<()> { + let usr = path.get_usr().ok_or_else(|| anyhow!("no usr"))?; + let parent_path = path + .get_semantic_parent() + .ok_or_else(|| anyhow!("Trying to insert the global namespace"))?; + + self.get_dyn_mut(parent_path) + .map(|described_entity| described_entity.entity) + // TODO: Properly define an error + .ok_or_else(|| anyhow!("has parent: {:?} but no parent in tree", parent_path))? + .map(|parent| parent.insert(usr, entity)) } - fn get_member_functions(&mut self) -> Option<&mut BTreeMap>> { - Some(&mut self.member_functions) + + pub(super) fn insert(&mut self, path: clang::Entity, entity: Described) -> Result<()> + where + T: Entity, + { + self.insert_dyn(path, entity.into_dyn()) } - fn get_member_typedefs(&mut self) -> Option<&mut BTreeMap>> { - Some(&mut self.member_typedefs) + */ + + // 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!(), + } } - fn get_member_enums(&mut self) -> Option<&mut BTreeMap>> { - Some(&mut self.member_enums) + + /* + pub(super) fn entry( + &mut self, + path: clang::Entity, + ) -> Result, TopLevelEntryError> + where + T: Entity, + { + Ok(match self.entry_dyn(path)? { + EntityEntry::Occupied { entity } => EntityEntry::Occupied { + entity: DescribedRefMut::from_dyn_mut(entity).unwrap(), + }, + EntityEntry::Vacant { parent } => EntityEntry::Vacant { parent }, + }) } + */ +} + +// 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, Clone, Copy)] +pub(super) enum DynEntityRef<'a> { + Namespace(&'a Namespace), + Variable(&'a Variable), + Struct(&'a Struct), + Function(&'a Function), + Enum(&'a Enum), + EnumConstant(&'a EnumConstant), + TypeAlias(&'a TypeAlias), } /* -impl FromNamespaceParent for Namespace { - fn from_namespace_parent<'a>( - parent: &'a mut Described, - name: &Usr, - ) -> Option<&'a mut Described> { - parent.entity.member_namespaces.get_mut(name) +impl<'a> DynEntityRef<'a> { + fn map(&self, f: F) -> U + where + F: FnOnce(&'a dyn Traversable) -> U, + { + match self { + DynEntityRef::Namespace(namespace) => f(*namespace), + DynEntityRef::Variable(variable) => f(*variable), + DynEntityRef::Struct(r#struct) => f(*r#struct), + DynEntityRef::Function(function) => f(*function), + DynEntityRef::Enum(Enum) => f(*Enum), + DynEntityRef::EnumConstant(enum_constant) => f(*enum_constant), + DynEntityRef::TypeAlias(type_alias) => f(*type_alias), + } } } */ -impl From for PartialEntity { - fn from(entity: Namespace) -> Self { - let mut children = Default::default(); - append_children(&mut children, entity.member_namespaces); - append_children(&mut children, entity.member_variables); - append_children(&mut children, entity.member_structs); - append_children(&mut children, entity.member_functions); - append_children(&mut children, entity.member_typedefs); - append_children(&mut children, entity.member_enums); - - PartialEntity { - properties: Default::default(), - children, +#[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), } } -} -#[derive(Debug, Clone)] -pub(super) struct Variable { - pub(super) kind: VariableKind, - pub(super) r#type: String, + 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 ClangEntity for Variable { - fn kind(&self) -> ClangEntityKind { - ClangEntityKind::Variable(self.kind) +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(), + }) } } -impl From for PartialEntity { - fn from(entity: Variable) -> Self { - let mut properties = Map::new(); - properties.insert(String::from("type"), JSONValue::String(entity.r#type)); - PartialEntity { - properties, - children: Default::default(), - } +#[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 FromNamespaceParent for Variable { - fn from_namespace_parent<'a>( - parent: &'a mut Described, - name: &Usr, - ) -> Option<&'a mut Described> { - parent.entity.member_variables.get_mut(name) +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 } } -*/ -#[derive(Debug, Clone)] -pub(super) struct Struct { - //pub(super) member_types: Vec, - pub(super) kind: StructKind, - pub(super) member_variables: BTreeMap>, - pub(super) member_structs: BTreeMap>, - pub(super) member_functions: BTreeMap>, - pub(super) member_typedefs: BTreeMap>, - pub(super) member_enums: BTreeMap>, +// ================= +// === 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 ClangEntity for Struct { - fn kind(&self) -> ClangEntityKind { - ClangEntityKind::Struct(self.kind) +impl Namespace { + fn new_global() -> Self { + Namespace { + global: true, + ..Default::default() + } } +} - fn get_member_variables(&mut self) -> Option<&mut BTreeMap>> { - Some(&mut self.member_variables) +impl KindName for Namespace { + fn kind_name_singular(&self) -> &'static str { + if self.global { + "global namespace" + } else { + "namespace" + } } - fn get_member_structs(&mut self) -> Option<&mut BTreeMap>> { - Some(&mut self.member_structs) + + fn kind_name_plural(&self) -> &'static str { + if self.global { + // TODO: probably panic here, branch should not happen + "global namespaces" + } else { + "namespaces" + } } - fn get_member_functions(&mut self) -> Option<&mut BTreeMap>> { - Some(&mut self.member_functions) +} + +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(), + } } - fn get_member_typedefs(&mut self) -> Option<&mut BTreeMap>> { - Some(&mut self.member_typedefs) +} + +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()) } - fn get_member_enums(&mut self) -> Option<&mut BTreeMap>> { - Some(&mut self.member_enums) +} + +// ================ +// === 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 From for PartialEntity { - fn from(entity: Struct) -> Self { - let mut children = Default::default(); - append_children(&mut children, entity.member_variables); - append_children(&mut children, entity.member_structs); - append_children(&mut children, entity.member_functions); - append_children(&mut children, entity.member_typedefs); - append_children(&mut children, entity.member_enums); +impl KindName for Variable { + fn kind_name_singular(&self) -> &'static str { + match self.kind { + VariableKind::Variable => "variable", + VariableKind::Field { .. } => "field", + } + } - PartialEntity { - properties: Default::default(), - children, + fn kind_name_plural(&self) -> &'static str { + match self.kind { + VariableKind::Variable => "variables", + VariableKind::Field { .. } => "fields", } } } -/* -impl FromNamespaceParent for Struct { - fn from_namespace_parent<'a>( - parent: &'a mut Described, - name: &Usr, - ) -> Option<&'a mut Described> { - parent.entity.member_structs.get_mut(name) +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 }) } } -*/ -#[derive(Debug, Clone)] -pub(super) struct Function { - pub(super) kind: FunctionKind, - pub(super) arguments: Vec, - pub(super) return_type: String, +// ============== +// === Struct === +// ============== + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(super) enum StructKind { + Struct, + Class, } -impl ClangEntity for Function { - fn kind(&self) -> ClangEntityKind { - ClangEntityKind::Function(self.kind) +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 From for PartialEntity { - fn from(entity: Function) -> Self { - let mut properties = Map::with_capacity(2); - properties.insert( - String::from("return-type"), - JSONValue::String(entity.return_type), - ); - properties.insert( - String::from("arguments"), - JSONValue::Array(entity.arguments.into_iter().map(Into::into).collect()), +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); - PartialEntity { - properties, - children: Default::default(), + 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 => 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, + }) } } -/* -impl FromNamespaceParent for Function { - fn from_namespace_parent<'a>( - parent: &'a mut Described, - name: &Usr, - ) -> Option<&'a mut Described> { - parent.entity.member_functions.get_mut(name) +// ================ +// === 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)] +#[derive(Debug, Clone, PartialEq, Eq)] pub(super) struct FunctionArgument { pub(super) name: String, pub(super) r#type: String, } -impl From for JSONValue { - fn from(argument: FunctionArgument) -> JSONValue { - let mut object = Map::new(); - object.insert(String::from("type"), JSONValue::String(argument.r#type)); - object.insert(String::from("name"), JSONValue::String(argument.name)); - JSONValue::Object(object) +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", + } } } -#[derive(Debug, Clone)] -pub(super) struct Typedef { - pub(super) referee: String, +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, + }) + } } -impl ClangEntity for Typedef { - fn kind(&self) -> ClangEntityKind { - ClangEntityKind::Typedef +// ============ +// === Enum === +// ============ + +entity! { + pub(super) struct Enum { + underlying_type: String, + #children => [ + constants: EnumConstant, + ], } } -impl From for PartialEntity { - fn from(entity: Typedef) -> Self { - let mut properties = Map::with_capacity(1); - properties.insert(String::from("referee"), JSONValue::String(entity.referee)); +impl KindName for Enum { + fn kind_name_singular(&self) -> &'static str { + "enum" + } + + fn kind_name_plural(&self) -> &'static str { + "enums" + } +} - PartialEntity { - properties, - children: Default::default(), +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, + }) } } -#[derive(Debug, Clone)] -pub(super) struct Enum { - pub(super) underlying_type: String, - pub(super) constants: BTreeMap>, +// ====================== +// === Enum Constants === +// ====================== + +entity! { + pub(super) struct EnumConstant { + value: String, + } } -impl ClangEntity for Enum { - fn kind(&self) -> ClangEntityKind { - ClangEntityKind::Enum +impl KindName for EnumConstant { + fn kind_name_singular(&self) -> &'static str { + "enum variant" + } + + fn kind_name_plural(&self) -> &'static str { + "enum variants" } } -impl From for PartialEntity { - fn from(entity: Enum) -> Self { - let mut properties = Map::with_capacity(1); - properties.insert( - String::from("underlying-type"), - JSONValue::String(entity.underlying_type), +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 }) + } +} - let mut children = Default::default(); - append_children(&mut children, entity.constants); +// ================== +// === Type Alias === +// ================== - PartialEntity { - properties, - children, - } +entity! { + pub(super) struct TypeAlias { + referee: String, } } -#[derive(Debug, Clone)] -pub(super) struct EnumConstant { - pub(super) value: String, -} +impl KindName for TypeAlias { + fn kind_name_singular(&self) -> &'static str { + "type alias" + } -impl ClangEntity for EnumConstant { - fn kind(&self) -> ClangEntityKind { - ClangEntityKind::EnumConstant + fn kind_name_plural(&self) -> &'static str { + "type aliases" } } -impl From for PartialEntity { - fn from(entity: EnumConstant) -> Self { - let mut properties = Map::with_capacity(1); - properties.insert(String::from("value"), JSONValue::String(entity.value)); +impl<'a> TryFrom> for TypeAlias { + type Error = Error; - PartialEntity { - properties, - children: Default::default(), - } + 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 }) } } -- cgit v1.2.3