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/generator/mod.rs | 23 +- src/parser/clang/entities.rs | 1477 +++++++++++++++++++++++++++++++----------- src/parser/clang/parsing.rs | 636 +----------------- src/types.rs | 2 +- 4 files changed, 1147 insertions(+), 991 deletions(-) diff --git a/src/generator/mod.rs b/src/generator/mod.rs index 3f62779..f351bb1 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -17,7 +17,7 @@ const DEFAULT_CSS: &[u8] = include_bytes!("../../res/style.css"); pub(crate) fn generate( base_dir: &Path, - entities: BTreeMap, + toplevel_entity: Entity, config: &Config, ) -> Result<()> { let md_output_dir = base_dir.join("markdown"); @@ -38,17 +38,16 @@ pub(crate) fn generate( debug!("Generated temporary file with CSS at: {:?}", css_path); rayon::scope(|scope| { - for (id, entity) in entities { - generate_recursively( - id, - entity, - scope, - &md_output_dir, - &html_output_dir, - css_path, - config, - ); - } + generate_recursively( + // TODO: a bit hacky? + EntityId(String::from("index")), + toplevel_entity, + scope, + &md_output_dir, + &html_output_dir, + css_path, + config, + ); }); Ok(()) 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 }) } } diff --git a/src/parser/clang/parsing.rs b/src/parser/clang/parsing.rs index 50ec5d9..82f0a94 100644 --- a/src/parser/clang/parsing.rs +++ b/src/parser/clang/parsing.rs @@ -1,240 +1,18 @@ use super::config::Config; use super::diagnostics; use super::entities::*; -use crate::types::*; +use crate::types; -use anyhow::{anyhow, Context, Error, Result}; -use clang::{Clang, CompilationDatabase, Index, TranslationUnit, Usr}; +use anyhow::{Context, Result}; +use clang::{Clang, CompilationDatabase, Index, TranslationUnit}; use thiserror::Error; -use std::collections::BTreeMap; use std::convert::{TryFrom, TryInto}; use std::path::{Path, PathBuf}; // TODO: check for use of Extend instead of loop+insert -#[derive(Debug, Default)] -struct TopLevel { - namespaces: BTreeMap>, - variables: BTreeMap>, - structs: BTreeMap>, - functions: BTreeMap>, - typedefs: BTreeMap>, - enums: BTreeMap>, -} - -/* -enum TopLevelEntry<'a, T> { - Vacant { - parent: &'a mut Described, - }, - /// Vacant, but no semantic parent - TopLevel, - Occupied { - entity: &'a mut Described, - }, - Error, -} -*/ - -impl TopLevel { - // Somehow has a lifetime issue I can't get my head around - /* - fn entry<'a, T>(&'a mut self, path: &::clang::Entity) -> Result> - where - T: ClangEntity + FromNamespaceParent + FromTopLevel, - { - let usr = path.get_usr().ok_or_else(|| anyhow!("no usr"))?; - if let Some(parent_path) = parent(&path) { - let parent_entry = self.entry::(&parent_path)?; - if let TopLevelEntry::Occupied { - entity: namespace_parent, - } = parent_entry - { - Ok(match T::from_namespace_parent(namespace_parent, &usr) { - None => TopLevelEntry::Vacant { - parent: namespace_parent, - }, - Some(entity) => TopLevelEntry::Occupied { entity }, - }) - } else { - panic!("Wut"); - } - } else { - Ok(match T::from_toplevel(self, &usr) { - Some(entity) => TopLevelEntry::Occupied { entity }, - None => TopLevelEntry::TopLevel, - }) - } - } - */ - - fn get_entity_mut(&mut self, path: clang::Entity) -> Option<&mut dyn ClangEntity> { - let usr = path.get_usr()?; - if let Some(parent_path) = parent(path) { - let parent = self.get_entity_mut(parent_path)?; - Some(match path.get_kind().try_into().ok()? { - ClangEntityKind::Namespace => { - &mut parent.get_member_namespaces()?.get_mut(&usr)?.entity - } - ClangEntityKind::Variable(_) => { - &mut parent.get_member_variables()?.get_mut(&usr)?.entity - } - ClangEntityKind::Function(_) => { - &mut parent.get_member_functions()?.get_mut(&usr)?.entity - } - ClangEntityKind::Struct(_) => { - &mut parent.get_member_structs()?.get_mut(&usr)?.entity - } - ClangEntityKind::Typedef => { - &mut parent.get_member_typedefs()?.get_mut(&usr)?.entity - } - ClangEntityKind::Enum => &mut parent.get_member_enums()?.get_mut(&usr)?.entity, - ClangEntityKind::EnumConstant => { - panic!("enum variant getting"); - } - }) - } else { - Some(match path.get_kind().try_into().ok()? { - ClangEntityKind::Namespace => &mut self.namespaces.get_mut(&usr)?.entity, - ClangEntityKind::Variable(_) => &mut self.variables.get_mut(&usr)?.entity, - ClangEntityKind::Struct(_) => &mut self.structs.get_mut(&usr)?.entity, - ClangEntityKind::Function(_) => &mut self.functions.get_mut(&usr)?.entity, - ClangEntityKind::Typedef => &mut self.typedefs.get_mut(&usr)?.entity, - ClangEntityKind::Enum => &mut self.enums.get_mut(&usr)?.entity, - ClangEntityKind::EnumConstant => panic!("enum variant getting"), - }) - } - } - - fn get_namespace_mut(&mut self, path: clang::Entity) -> Option<&mut Described> { - let usr = path.get_usr()?; - - if let Some(parent_path) = parent(path) { - let parent = self.get_entity_mut(parent_path)?; - parent.get_member_namespaces()?.get_mut(&usr) - } else { - self.namespaces.get_mut(&usr) - } - } - - fn insert(&mut self, path: clang::Entity, entity: Described) -> Result<()> - where - T: ClangEntity + std::fmt::Debug, - Self: TopLevelManipulation, - Namespace: NamespaceParentManipulation, - { - let usr = path.get_usr().ok_or_else(|| anyhow!("no usr"))?; - if let Some(parent_path) = parent(path) { - if let Some(parent_namespace) = self.get_namespace_mut(parent_path) { - parent_namespace - .entity - .get_members_mut() - // Namespace should be able to contain every kind of entity - .unwrap() - .insert(usr, entity); - Ok(()) - } else { - Err(anyhow!( - "has parent: {:?} but no parent in tree", - parent_path - )) - } - } else { - self.insert_toplevel(usr, entity); - Ok(()) - } - } -} - -// Like .get_semantic_parent(), but return none if the parent is the translation unit -fn parent(libclang_entity: clang::Entity) -> Option { - match libclang_entity.get_semantic_parent() { - Some(parent) => { - if parent.get_kind() != clang::EntityKind::TranslationUnit { - Some(parent) - } else { - None - } - } - None => { - warn!("get_semantic_parent() returned None"); - None - } - } -} - -trait TopLevelManipulation { - fn insert_toplevel(&mut self, usr: Usr, entity: Described); -} - -impl TopLevelManipulation for TopLevel { - fn insert_toplevel(&mut self, usr: Usr, entity: Described) { - self.namespaces.insert(usr, entity); - } -} - -impl TopLevelManipulation for TopLevel { - fn insert_toplevel(&mut self, usr: Usr, entity: Described) { - self.variables.insert(usr, entity); - } -} - -impl TopLevelManipulation for TopLevel { - fn insert_toplevel(&mut self, usr: Usr, entity: Described) { - self.functions.insert(usr, entity); - } -} - -impl TopLevelManipulation for TopLevel { - fn insert_toplevel(&mut self, usr: Usr, entity: Described) { - self.structs.insert(usr, entity); - } -} - -impl TopLevelManipulation for TopLevel { - fn insert_toplevel(&mut self, usr: Usr, entity: Described) { - self.typedefs.insert(usr, entity); - } -} - -impl TopLevelManipulation for TopLevel { - fn insert_toplevel(&mut self, usr: Usr, entity: Described) { - self.enums.insert(usr, entity); - } -} - -/* -trait FromTopLevel: ClangEntity + Sized { - fn from_toplevel<'a>(toplevel: &'a mut TopLevel, usr: &Usr) -> Option<&'a mut Described>; -} - -impl FromTopLevel for Namespace { - fn from_toplevel<'a>(toplevel: &'a mut TopLevel, usr: &Usr) -> Option<&'a mut Described> { - toplevel.namespaces.get_mut(usr) - } -} - -impl FromTopLevel for Variable { - fn from_toplevel<'a>(toplevel: &'a mut TopLevel, usr: &Usr) -> Option<&'a mut Described> { - toplevel.variables.get_mut(usr) - } -} - -impl FromTopLevel for Function { - fn from_toplevel<'a>(toplevel: &'a mut TopLevel, usr: &Usr) -> Option<&'a mut Described> { - toplevel.functions.get_mut(usr) - } -} - -impl FromTopLevel for Struct { - fn from_toplevel<'a>(toplevel: &'a mut TopLevel, usr: &Usr) -> Option<&'a mut Described> { - toplevel.structs.get_mut(usr) - } -} -*/ - -pub(crate) fn parse_compile_commands(config: &Config) -> Result> { +pub(crate) fn parse_compile_commands(config: &Config) -> Result { let clang = Clang::new().unwrap(); let index = Index::new( &clang, /* exclude from pch = */ false, /* print diagnostics = */ false, @@ -295,7 +73,7 @@ pub(crate) fn parse_compile_commands(config: &Config) -> Result(path: T, config: &Config) -> Result> +pub(crate) fn parse_file(path: T, config: &Config) -> Result where T: Into, T: AsRef, @@ -422,15 +200,13 @@ fn is_in_system_header(entity: clang::Entity, base_dir: impl AsRef) -> boo } // Entries encountered in the toplevel lexical context -fn add_entity( - libclang_entity: clang::Entity, - toplevel: &mut TopLevel, -) -> clang::EntityVisitResult { - if libclang_entity.get_usr().is_none() { - return clang::EntityVisitResult::Continue; +fn add_entity(libclang_entity: clang::Entity, toplevel: &mut TopLevel) -> clang::EntityVisitResult { + let usr = match libclang_entity.get_usr() { + None => return clang::EntityVisitResult::Continue, + Some(usr) => usr, }; - let kind = match ClangEntityKind::try_from(libclang_entity.get_kind()) { + let kind = match EntityKind::try_from(libclang_entity.get_kind()) { Ok(kind) => kind, Err(err) => { diagnostics::error(format!("{}", err), libclang_entity); @@ -438,51 +214,29 @@ fn add_entity( } }; - if let Some(in_tree_entity) = toplevel.get_entity_mut(libclang_entity) { - // if current.has_documentation && !tree.has_documentation { - // append_documentation - // } - } else { - //if libclang_entity.is_definition() { - // TODO: This probably means that you can't put documentation in forward declarations. - // TODO: Ad-hoc toplevel functions are not documented - // - // This seems restrictive, but since there can be multiple declarations but only one definition, - // you should probably put your documentation on the definition anyway? - // - // Also, skipping forward declarations allows us to not have to insert, then update the tree - // when we see the definition. - - let result = match kind { - ClangEntityKind::Namespace => { - if libclang_entity.get_name().is_some() { - Described::::try_from(libclang_entity) - .and_then(|namespace| toplevel.insert(libclang_entity, namespace)) - } else { - Ok(()) - } + match toplevel.entry_dyn(libclang_entity) { + Err(err) => diagnostics::error(format!("{}", err), libclang_entity), + Ok(EntityEntry::Occupied { entity }) => { + // if current.has_documentation && !tree.has_documentation { + // append_documentation + // } + // + // if !curent.has_children && tree.has_children { + // parse(tree) + // } + } + Ok(EntityEntry::Vacant { parent: dyn_parent }) => { + if let Err(err) = libclang_entity + .try_into() + .and_then(|entity| dyn_parent.entity.insert(usr, entity)) + { + diagnostics::error(format!("{}", err), libclang_entity); + return ::clang::EntityVisitResult::Continue; } - ClangEntityKind::Variable(_) => Described::::try_from(libclang_entity) - .and_then(|variable| toplevel.insert(libclang_entity, variable)), - ClangEntityKind::Struct(_) => Described::::try_from(libclang_entity) - .and_then(|r#struct| toplevel.insert(libclang_entity, r#struct)), - ClangEntityKind::Function(_) => Described::::try_from(libclang_entity) - .and_then(|function| toplevel.insert(libclang_entity, function)), - ClangEntityKind::Typedef => Described::::try_from(libclang_entity) - .and_then(|typedef| toplevel.insert(libclang_entity, typedef)), - ClangEntityKind::Enum => Described::::try_from(libclang_entity) - .and_then(|r#enum| toplevel.insert(libclang_entity, r#enum)), - ClangEntityKind::EnumConstant => panic!("Enum variant encountered not within an enum"), - }; - // TODO: check result - - if let Err(err) = result { - diagnostics::error(format!("{}", err), libclang_entity); - return ::clang::EntityVisitResult::Continue; } } - if kind == ClangEntityKind::Namespace && libclang_entity.get_name().is_some() { + if kind == EntityKind::Namespace && libclang_entity.get_name().is_some() { // Recurse here since namespace definitions are allowed to change between translation units. ::clang::EntityVisitResult::Recurse } else { @@ -490,338 +244,6 @@ fn add_entity( } } -impl From for BTreeMap { - fn from(toplevel: TopLevel) -> Self { - toplevel - .namespaces - .into_iter() - .map(|(usr, entity)| (EntityId(usr.0), entity.into())) - .chain( - toplevel - .variables - .into_iter() - .map(|(usr, entity)| (EntityId(usr.0), entity.into())), - ) - .chain( - toplevel - .structs - .into_iter() - .map(|(usr, entity)| (EntityId(usr.0), entity.into())), - ) - .chain( - toplevel - .functions - .into_iter() - .map(|(usr, entity)| (EntityId(usr.0), entity.into())), - ) - .chain( - toplevel - .typedefs - .into_iter() - .map(|(usr, entity)| (EntityId(usr.0), entity.into())), - ) - .collect() - } -} - -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: get_description(entity)?, - entity: T::try_from(entity)?, - }) - } -} - -impl<'a> TryFrom> for Namespace { - type Error = Error; - - fn try_from(entity: clang::Entity) -> Result { - match entity.get_kind().try_into() { - Ok(ClangEntityKind::Namespace) => {} - _ => panic!("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 { - member_namespaces: Default::default(), - member_variables: Default::default(), - member_structs: Default::default(), - member_functions: Default::default(), - member_typedefs: Default::default(), - member_enums: Default::default(), - }) - } -} - -impl<'a> TryFrom> for Variable { - type Error = Error; - - fn try_from(entity: clang::Entity) -> Result { - let variable_kind; - match entity.get_kind().try_into() { - Ok(ClangEntityKind::Variable(kind)) => { - variable_kind = kind; - } - _ => panic!("Trying to parse a non-variable into a variable"), - } - debug!("Parsing Variable: {:?}", entity); - - let r#type = entity.get_type().unwrap().get_display_name(); - trace!("Variable has type: {:?}", r#type); - - Ok(Variable { - r#type, - kind: variable_kind, - }) - } -} - -impl<'a> TryFrom> for Struct { - type Error = Error; - - fn try_from(entity: clang::Entity) -> Result { - let struct_kind; - match entity.get_kind().try_into() { - Ok(ClangEntityKind::Struct(kind)) => { - struct_kind = kind; - } - _ => panic!("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_typedefs = BTreeMap::new(); - let mut member_enums = 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(ClangEntityKind::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(ClangEntityKind::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(ClangEntityKind::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(ClangEntityKind::Typedef) => { - let child_usr = child - .get_usr() - .ok_or_else(|| anyhow!("no usr for: {:?}", child))?; - member_typedefs.insert(child_usr, Described::::try_from(child)?); - } - Ok(ClangEntityKind::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(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); - } - } - } - - Ok(Struct { - kind: struct_kind, - member_functions, - member_structs, - member_variables, - member_typedefs, - member_enums, - }) - } -} - -impl<'a> TryFrom> for Function { - type Error = Error; - - fn try_from(entity: clang::Entity) -> Result { - let function_kind; - match entity.get_kind().try_into() { - Ok(ClangEntityKind::Function(kind)) => { - function_kind = kind; - } - _ => panic!("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(); - - Ok(Function { - kind: function_kind, - arguments, - return_type, - }) - } -} - -impl<'a> TryFrom> for Typedef { - type Error = Error; - - fn try_from(entity: clang::Entity) -> Result { - match entity.get_kind().try_into() { - Ok(ClangEntityKind::Typedef) => {} - _ => panic!("Trying to parse a non-typedef into a typedef"), - } - debug!("Parsing typedef: {:?}", entity); - - let referee = entity - .get_typedef_underlying_type() - .ok_or_else(|| anyhow!("No underlying type"))? - .get_display_name(); - - Ok(Typedef { referee }) - } -} - -impl<'a> TryFrom> for Enum { - type Error = Error; - - fn try_from(entity: clang::Entity) -> Result { - match entity.get_kind().try_into() { - Ok(ClangEntityKind::Enum) => {} - _ => panic!("Trying to parse a non-enum into a enum"), - } - 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(ClangEntityKind::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, - }) - } -} - -impl<'a> TryFrom> for EnumConstant { - type Error = Error; - - fn try_from(entity: clang::Entity) -> Result { - match entity.get_kind().try_into() { - Ok(ClangEntityKind::EnumConstant) => {} - _ => panic!("Trying to parse a non-enum constant into a enum constant"), - } - 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 }) - } -} - -fn get_description(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: parse_comment(comment), - }) - } else { - Ok(Description { - name, - brief: String::new(), - detailed: String::new(), - }) - } -} - pub fn parse_comment(raw: String) -> String { #[derive(Debug)] enum CommentStyle { diff --git a/src/types.rs b/src/types.rs index 4099714..33a86c6 100644 --- a/src/types.rs +++ b/src/types.rs @@ -13,7 +13,7 @@ use std::str::FromStr; #[non_exhaustive] pub struct Poseidoc { pub config: toml::Value, - pub entities: BTreeMap, + pub toplevel_entity: Entity, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -- cgit v1.2.3