summaryrefslogtreecommitdiffstats
path: root/src/parser
diff options
context:
space:
mode:
authorMinijackson <minijackson@riseup.net>2019-12-21 12:13:21 +0100
committerMinijackson <minijackson@riseup.net>2019-12-21 12:13:21 +0100
commite773caea8010b87726ea524d31798fb2e43e12f4 (patch)
tree9034295831afb25f4bfea5a05d9f83d03e69e86c /src/parser
parentde896baff7e97fac4dde79078c9a2fa1c652576b (diff)
downloadposeidoc-e773caea8010b87726ea524d31798fb2e43e12f4.tar.gz
poseidoc-e773caea8010b87726ea524d31798fb2e43e12f4.zip
newtype in types, more generator config, parsing -> parser
Diffstat (limited to 'src/parser')
-rw-r--r--src/parser/clang/config.rs53
-rw-r--r--src/parser/clang/entities.rs390
-rw-r--r--src/parser/clang/mod.rs5
-rw-r--r--src/parser/clang/parsing.rs764
-rw-r--r--src/parser/mod.rs1
5 files changed, 1213 insertions, 0 deletions
diff --git a/src/parser/clang/config.rs b/src/parser/clang/config.rs
new file mode 100644
index 0000000..05fe3ab
--- /dev/null
+++ b/src/parser/clang/config.rs
@@ -0,0 +1,53 @@
1use anyhow::Result;
2use serde_derive::{Deserialize, Serialize};
3use structopt::StructOpt;
4
5use std::path::PathBuf;
6
7#[derive(Debug, Clone, StructOpt, Deserialize, Serialize)]
8#[serde(rename_all = "kebab-case")]
9pub(crate) struct Config {
10 pub(super) compile_commands_location: PathBuf,
11 pub(super) extra_args: Vec<String>,
12}
13
14#[derive(Debug, Clone, Default, StructOpt, Deserialize, Serialize)]
15#[serde(rename_all = "kebab-case")]
16pub(crate) struct ProvidedConfig {
17 #[structopt(long = "clang-compile-commands-location")]
18 pub(super) compile_commands_location: Option<PathBuf>,
19 #[structopt(long = "clang-extra-args", number_of_values = 1)]
20 #[serde(default)]
21 pub(super) extra_args: Vec<String>,
22}
23
24impl Default for Config {
25 fn default() -> Self {
26 Config::from_merge(
27 ProvidedConfig::default(),
28 ProvidedConfig::default(),
29 )
30 // Currently errors out only on CLI parse fail with clang's extra args
31 .unwrap()
32 }
33}
34
35impl Config {
36 pub(crate) fn from_merge(
37 cli: ProvidedConfig,
38 mut config: ProvidedConfig,
39 ) -> Result<Self> {
40 let mut extra_args = Vec::new();
41 for args in cli.extra_args {
42 extra_args.append(&mut ::shell_words::split(&args)?);
43 }
44 extra_args.append(&mut config.extra_args);
45 Ok(Self {
46 compile_commands_location: cli
47 .compile_commands_location
48 .or(config.compile_commands_location)
49 .unwrap_or_else(|| PathBuf::from(r".")),
50 extra_args,
51 })
52 }
53}
diff --git a/src/parser/clang/entities.rs b/src/parser/clang/entities.rs
new file mode 100644
index 0000000..41fe061
--- /dev/null
+++ b/src/parser/clang/entities.rs
@@ -0,0 +1,390 @@
1use crate::types::*;
2
3use clang::{EntityKind, Usr};
4use thiserror::Error;
5
6use std::collections::BTreeMap;
7use std::convert::TryFrom;
8
9// TODO: factor out hardcoded strings
10
11#[derive(Debug)]
12pub(super) struct Children {
13 namespaces: Option<BTreeMap<Usr, Described<Namespace>>>,
14 variables: Option<BTreeMap<Usr, Described<Variable>>>,
15 structs: Option<BTreeMap<Usr, Described<Struct>>>,
16 functions: Option<BTreeMap<Usr, Described<Function>>>,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub(super) enum ClangEntityKind {
21 Namespace,
22 Variable(VariableKind),
23 Struct(StructKind),
24 Function(FunctionKind),
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub(super) enum VariableKind {
29 Variable,
30 Field,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub(super) enum StructKind {
35 Struct,
36 Class,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub(super) enum FunctionKind {
41 Function,
42 Method,
43}
44
45impl ClangEntityKind {
46 fn to_str_singular(self) -> &'static str {
47 match self {
48 ClangEntityKind::Namespace => "namespace",
49 ClangEntityKind::Variable(VariableKind::Variable) => "variable",
50 ClangEntityKind::Variable(VariableKind::Field) => "field",
51 ClangEntityKind::Struct(StructKind::Struct) => "struct",
52 ClangEntityKind::Struct(StructKind::Class) => "class",
53 ClangEntityKind::Function(FunctionKind::Function) => "function",
54 ClangEntityKind::Function(FunctionKind::Method) => "method",
55 }
56 }
57
58 fn to_str_plural(self) -> &'static str {
59 match self {
60 ClangEntityKind::Namespace => "namespaces",
61 ClangEntityKind::Variable(VariableKind::Variable) => "variables",
62 ClangEntityKind::Variable(VariableKind::Field) => "fields",
63 ClangEntityKind::Struct(StructKind::Struct) => "structs",
64 ClangEntityKind::Struct(StructKind::Class) => "classes",
65 ClangEntityKind::Function(FunctionKind::Function) => "functions",
66 ClangEntityKind::Function(FunctionKind::Method) => "methods",
67 }
68 }
69}
70
71#[derive(Debug, Error)]
72#[error("Unsupported Clang entity kind: {:?}", _0)]
73pub(super) struct TryFromEntityKindError(String);
74
75impl TryFrom<EntityKind> for ClangEntityKind {
76 type Error = TryFromEntityKindError;
77
78 fn try_from(kind: EntityKind) -> Result<ClangEntityKind, TryFromEntityKindError> {
79 Ok(match kind {
80 EntityKind::Namespace => ClangEntityKind::Namespace,
81 EntityKind::VarDecl => ClangEntityKind::Variable(VariableKind::Variable),
82 EntityKind::FieldDecl => ClangEntityKind::Variable(VariableKind::Field),
83 EntityKind::FunctionDecl => ClangEntityKind::Function(FunctionKind::Function),
84 EntityKind::Method | EntityKind::Constructor => {
85 ClangEntityKind::Function(FunctionKind::Method)
86 }
87 EntityKind::StructDecl => ClangEntityKind::Struct(StructKind::Struct),
88 EntityKind::ClassDecl => ClangEntityKind::Struct(StructKind::Class),
89 kind => return Err(TryFromEntityKindError(format!("{:?}", kind))),
90 })
91 }
92}
93
94pub(super) trait ClangEntity {
95 fn kind(&self) -> ClangEntityKind;
96
97 fn get_member_namespaces(&mut self) -> Option<&mut BTreeMap<Usr, Described<Namespace>>> {
98 None
99 }
100 fn get_member_variables(&mut self) -> Option<&mut BTreeMap<Usr, Described<Variable>>> {
101 None
102 }
103 fn get_member_structs(&mut self) -> Option<&mut BTreeMap<Usr, Described<Struct>>> {
104 None
105 }
106 fn get_member_functions(&mut self) -> Option<&mut BTreeMap<Usr, Described<Function>>> {
107 None
108 }
109
110 fn into_children(self) -> Children
111 where
112 Self: Sized,
113 {
114 Children {
115 namespaces: None,
116 variables: None,
117 structs: None,
118 functions: None,
119 }
120 }
121}
122
123/*
124pub(super) trait FromNamespaceParent: ClangEntity + Sized {
125 fn from_namespace_parent<'a>(
126 parent: &'a mut Described<Namespace>,
127 name: &Usr,
128 ) -> Option<&'a mut Described<Self>>;
129}
130*/
131
132pub(super) trait NamespaceParentManipulation<T: ClangEntity + Sized> {
133 fn get_members_mut(&mut self) -> Option<&mut BTreeMap<Usr, Described<T>>>;
134}
135
136impl<U> NamespaceParentManipulation<Namespace> for U
137where
138 U: ClangEntity,
139{
140 fn get_members_mut(&mut self) -> Option<&mut BTreeMap<Usr, Described<Namespace>>> {
141 self.get_member_namespaces()
142 }
143}
144
145impl<U> NamespaceParentManipulation<Variable> for U
146where
147 U: ClangEntity,
148{
149 fn get_members_mut(&mut self) -> Option<&mut BTreeMap<Usr, Described<Variable>>> {
150 self.get_member_variables()
151 }
152}
153
154impl<U> NamespaceParentManipulation<Function> for U
155where
156 U: ClangEntity,
157{
158 fn get_members_mut(&mut self) -> Option<&mut BTreeMap<Usr, Described<Function>>> {
159 self.get_member_functions()
160 }
161}
162
163impl<U> NamespaceParentManipulation<Struct> for U
164where
165 U: ClangEntity,
166{
167 fn get_members_mut(&mut self) -> Option<&mut BTreeMap<Usr, Described<Struct>>> {
168 self.get_member_structs()
169 }
170}
171
172impl<T: ClangEntity> From<Described<T>> for Entity {
173 fn from(entity: Described<T>) -> Self {
174 let mut children: BTreeMap<ChildrenKind, BTreeMap<EntityId, Entity>> = BTreeMap::new();
175
176 let kind = EntityKind(String::from(entity.entity.kind().to_str_singular()));
177 let clang_children = entity.entity.into_children();
178
179 if let Some(namespaces) = clang_children.namespaces {
180 for (usr, namespace) in namespaces {
181 children
182 .entry(ChildrenKind(String::from(
183 namespace.entity.kind().to_str_plural(),
184 )))
185 .or_default()
186 .insert(EntityId(usr.0), namespace.into());
187 }
188 }
189
190 if let Some(variables) = clang_children.variables {
191 for (usr, variable) in variables {
192 children
193 .entry(ChildrenKind(String::from(
194 variable.entity.kind().to_str_plural(),
195 )))
196 .or_default()
197 .insert(EntityId(usr.0), variable.into());
198 }
199 }
200
201 if let Some(structs) = clang_children.structs {
202 for (usr, r#struct) in structs {
203 children
204 .entry(ChildrenKind(String::from(
205 r#struct.entity.kind().to_str_plural(),
206 )))
207 .or_default()
208 .insert(EntityId(usr.0), r#struct.into());
209 }
210 }
211
212 if let Some(functions) = clang_children.functions {
213 for (usr, function) in functions {
214 children
215 .entry(ChildrenKind(String::from(
216 function.entity.kind().to_str_plural(),
217 )))
218 .or_default()
219 .insert(EntityId(usr.0), function.into());
220 }
221 }
222
223 Entity {
224 name: entity.description.name,
225 language: Language::Clang,
226 kind,
227 brief_description: entity.description.brief,
228 documentation: entity.description.detailed,
229 children,
230 }
231 }
232}
233
234#[derive(Debug, Clone, Default)]
235pub(super) struct Description {
236 pub(super) name: String,
237 pub(super) brief: String,
238 pub(super) detailed: String,
239}
240
241#[derive(Debug, Clone)]
242pub(super) struct Described<T> {
243 pub(super) description: Description,
244 pub(super) entity: T,
245}
246
247#[derive(Debug, Clone)]
248pub(super) struct Namespace {
249 pub(super) member_namespaces: BTreeMap<Usr, Described<Namespace>>,
250 pub(super) member_variables: BTreeMap<Usr, Described<Variable>>,
251 pub(super) member_structs: BTreeMap<Usr, Described<Struct>>,
252 pub(super) member_functions: BTreeMap<Usr, Described<Function>>,
253}
254
255impl ClangEntity for Namespace {
256 fn kind(&self) -> ClangEntityKind {
257 ClangEntityKind::Namespace
258 }
259
260 fn get_member_namespaces(&mut self) -> Option<&mut BTreeMap<Usr, Described<Namespace>>> {
261 Some(&mut self.member_namespaces)
262 }
263 fn get_member_variables(&mut self) -> Option<&mut BTreeMap<Usr, Described<Variable>>> {
264 Some(&mut self.member_variables)
265 }
266 fn get_member_structs(&mut self) -> Option<&mut BTreeMap<Usr, Described<Struct>>> {
267 Some(&mut self.member_structs)
268 }
269 fn get_member_functions(&mut self) -> Option<&mut BTreeMap<Usr, Described<Function>>> {
270 Some(&mut self.member_functions)
271 }
272
273 fn into_children(self) -> Children {
274 Children {
275 namespaces: Some(self.member_namespaces),
276 variables: Some(self.member_variables),
277 structs: Some(self.member_structs),
278 functions: Some(self.member_functions),
279 }
280 }
281}
282
283/*
284impl FromNamespaceParent for Namespace {
285 fn from_namespace_parent<'a>(
286 parent: &'a mut Described<Namespace>,
287 name: &Usr,
288 ) -> Option<&'a mut Described<Self>> {
289 parent.entity.member_namespaces.get_mut(name)
290 }
291}
292*/
293
294#[derive(Debug, Clone)]
295pub(super) struct Variable {
296 pub(super) kind: VariableKind,
297 pub(super) r#type: String,
298}
299
300impl ClangEntity for Variable {
301 fn kind(&self) -> ClangEntityKind {
302 ClangEntityKind::Variable(self.kind)
303 }
304}
305
306/*
307impl FromNamespaceParent for Variable {
308 fn from_namespace_parent<'a>(
309 parent: &'a mut Described<Namespace>,
310 name: &Usr,
311 ) -> Option<&'a mut Described<Self>> {
312 parent.entity.member_variables.get_mut(name)
313 }
314}
315*/
316
317#[derive(Debug, Clone)]
318pub(super) struct Struct {
319 //pub(super) member_types: Vec<Usr>,
320 pub(super) kind: StructKind,
321 pub(super) member_variables: BTreeMap<Usr, Described<Variable>>,
322 pub(super) member_structs: BTreeMap<Usr, Described<Struct>>,
323 pub(super) member_functions: BTreeMap<Usr, Described<Function>>,
324}
325
326impl ClangEntity for Struct {
327 fn kind(&self) -> ClangEntityKind {
328 ClangEntityKind::Struct(self.kind)
329 }
330
331 fn get_member_variables(&mut self) -> Option<&mut BTreeMap<Usr, Described<Variable>>> {
332 Some(&mut self.member_variables)
333 }
334 fn get_member_structs(&mut self) -> Option<&mut BTreeMap<Usr, Described<Struct>>> {
335 Some(&mut self.member_structs)
336 }
337 fn get_member_functions(&mut self) -> Option<&mut BTreeMap<Usr, Described<Function>>> {
338 Some(&mut self.member_functions)
339 }
340
341 fn into_children(self) -> Children {
342 Children {
343 namespaces: None,
344 variables: Some(self.member_variables),
345 structs: Some(self.member_structs),
346 functions: Some(self.member_functions),
347 }
348 }
349}
350
351/*
352impl FromNamespaceParent for Struct {
353 fn from_namespace_parent<'a>(
354 parent: &'a mut Described<Namespace>,
355 name: &Usr,
356 ) -> Option<&'a mut Described<Self>> {
357 parent.entity.member_structs.get_mut(name)
358 }
359}
360*/
361
362#[derive(Debug, Clone)]
363pub(super) struct Function {
364 pub(super) kind: FunctionKind,
365 pub(super) arguments: Vec<FunctionArgument>,
366 pub(super) return_type: String,
367}
368
369impl ClangEntity for Function {
370 fn kind(&self) -> ClangEntityKind {
371 ClangEntityKind::Function(self.kind)
372 }
373}
374
375/*
376impl FromNamespaceParent for Function {
377 fn from_namespace_parent<'a>(
378 parent: &'a mut Described<Namespace>,
379 name: &Usr,
380 ) -> Option<&'a mut Described<Self>> {
381 parent.entity.member_functions.get_mut(name)
382 }
383}
384*/
385
386#[derive(Debug, Clone)]
387pub(super) struct FunctionArgument {
388 pub(super) name: String,
389 pub(super) r#type: String,
390}
diff --git a/src/parser/clang/mod.rs b/src/parser/clang/mod.rs
new file mode 100644
index 0000000..047c49e
--- /dev/null
+++ b/src/parser/clang/mod.rs
@@ -0,0 +1,5 @@
1pub(crate) mod config;
2mod entities;
3mod parsing;
4
5pub(crate) use parsing::parse_compile_commands;
diff --git a/src/parser/clang/parsing.rs b/src/parser/clang/parsing.rs
new file mode 100644
index 0000000..a654681
--- /dev/null
+++ b/src/parser/clang/parsing.rs
@@ -0,0 +1,764 @@
1use super::config::Config;
2use super::entities::*;
3use crate::types::*;
4
5use anyhow::{anyhow, Context, Error, Result};
6use clang::{Clang, CompilationDatabase, Index, TranslationUnit, Usr};
7use codemap::CodeMap;
8use thiserror::Error;
9
10use std::collections::BTreeMap;
11use std::convert::{TryFrom, TryInto};
12use std::path::{Path, PathBuf};
13
14#[derive(Debug, Default)]
15struct TopLevel {
16 namespaces: BTreeMap<Usr, Described<Namespace>>,
17 variables: BTreeMap<Usr, Described<Variable>>,
18 structs: BTreeMap<Usr, Described<Struct>>,
19 functions: BTreeMap<Usr, Described<Function>>,
20}
21
22/*
23enum TopLevelEntry<'a, T> {
24 Vacant {
25 parent: &'a mut Described<Namespace>,
26 },
27 /// Vacant, but no semantic parent
28 TopLevel,
29 Occupied {
30 entity: &'a mut Described<T>,
31 },
32 Error,
33}
34*/
35
36impl TopLevel {
37 // Somehow has a lifetime issue I can't get my head around
38 /*
39 fn entry<'a, T>(&'a mut self, path: &::clang::Entity) -> Result<TopLevelEntry<'a, T>>
40 where
41 T: ClangEntity + FromNamespaceParent + FromTopLevel,
42 {
43 let usr = path.get_usr().ok_or_else(|| anyhow!("no usr"))?;
44 if let Some(parent_path) = parent(&path) {
45 let parent_entry = self.entry::<Namespace>(&parent_path)?;
46 if let TopLevelEntry::Occupied {
47 entity: namespace_parent,
48 } = parent_entry
49 {
50 Ok(match T::from_namespace_parent(namespace_parent, &usr) {
51 None => TopLevelEntry::Vacant {
52 parent: namespace_parent,
53 },
54 Some(entity) => TopLevelEntry::Occupied { entity },
55 })
56 } else {
57 panic!("Wut");
58 }
59 } else {
60 Ok(match T::from_toplevel(self, &usr) {
61 Some(entity) => TopLevelEntry::Occupied { entity },
62 None => TopLevelEntry::TopLevel,
63 })
64 }
65 }
66 */
67
68 fn get_entity_mut(&mut self, path: clang::Entity) -> Option<&mut dyn ClangEntity> {
69 let usr = path.get_usr()?;
70 if let Some(parent_path) = parent(path) {
71 let parent = self.get_entity_mut(parent_path)?;
72 Some(match path.get_kind().try_into().ok()? {
73 ClangEntityKind::Namespace => {
74 &mut parent.get_member_namespaces()?.get_mut(&usr)?.entity
75 }
76 ClangEntityKind::Variable(_) => {
77 &mut parent.get_member_variables()?.get_mut(&usr)?.entity
78 }
79 ClangEntityKind::Function(_) => {
80 &mut parent.get_member_functions()?.get_mut(&usr)?.entity
81 }
82 ClangEntityKind::Struct(_) => {
83 &mut parent.get_member_structs()?.get_mut(&usr)?.entity
84 }
85 })
86 } else {
87 Some(match path.get_kind().try_into().ok()? {
88 ClangEntityKind::Namespace => &mut self.namespaces.get_mut(&usr)?.entity,
89 ClangEntityKind::Variable(_) => &mut self.variables.get_mut(&usr)?.entity,
90 ClangEntityKind::Struct(_) => &mut self.structs.get_mut(&usr)?.entity,
91 ClangEntityKind::Function(_) => &mut self.functions.get_mut(&usr)?.entity,
92 })
93 }
94 }
95
96 fn get_namespace_mut(&mut self, path: clang::Entity) -> Option<&mut Described<Namespace>> {
97 let usr = path.get_usr()?;
98
99 if let Some(parent_path) = parent(path) {
100 let parent = self.get_entity_mut(parent_path)?;
101 parent.get_member_namespaces()?.get_mut(&usr)
102 } else {
103 self.namespaces.get_mut(&usr)
104 }
105 }
106
107 fn insert<T>(&mut self, path: clang::Entity, entity: Described<T>) -> Result<()>
108 where
109 T: ClangEntity + std::fmt::Debug,
110 Self: TopLevelManipulation<T>,
111 Namespace: NamespaceParentManipulation<T>,
112 {
113 let usr = path.get_usr().ok_or_else(|| anyhow!("no usr"))?;
114 if let Some(parent_path) = parent(path) {
115 if let Some(parent_namespace) = self.get_namespace_mut(parent_path) {
116 parent_namespace
117 .entity
118 .get_members_mut()
119 // Namespace should be able to contain every kind of entity
120 .unwrap()
121 .insert(usr, entity);
122 Ok(())
123 } else {
124 Err(anyhow!("has parent but no parent in tree"))
125 }
126 } else {
127 self.insert_toplevel(usr, entity);
128 Ok(())
129 }
130 }
131}
132
133// Like .get_semantic_parent(), but return none if the parent is the translation unit
134fn parent(libclang_entity: clang::Entity) -> Option<clang::Entity> {
135 match libclang_entity.get_semantic_parent() {
136 Some(parent) => {
137 if parent.get_kind() != clang::EntityKind::TranslationUnit {
138 Some(parent)
139 } else {
140 None
141 }
142 }
143 None => {
144 warn!("get_semantic_parent() returned None");
145 None
146 }
147 }
148}
149
150trait TopLevelManipulation<T: ClangEntity> {
151 fn insert_toplevel(&mut self, usr: Usr, entity: Described<T>);
152}
153
154impl TopLevelManipulation<Namespace> for TopLevel {
155 fn insert_toplevel(&mut self, usr: Usr, entity: Described<Namespace>) {
156 self.namespaces.insert(usr, entity);
157 }
158}
159
160impl TopLevelManipulation<Variable> for TopLevel {
161 fn insert_toplevel(&mut self, usr: Usr, entity: Described<Variable>) {
162 self.variables.insert(usr, entity);
163 }
164}
165
166impl TopLevelManipulation<Function> for TopLevel {
167 fn insert_toplevel(&mut self, usr: Usr, entity: Described<Function>) {
168 self.functions.insert(usr, entity);
169 }
170}
171
172impl TopLevelManipulation<Struct> for TopLevel {
173 fn insert_toplevel(&mut self, usr: Usr, entity: Described<Struct>) {
174 self.structs.insert(usr, entity);
175 }
176}
177
178/*
179trait FromTopLevel: ClangEntity + Sized {
180 fn from_toplevel<'a>(toplevel: &'a mut TopLevel, usr: &Usr) -> Option<&'a mut Described<Self>>;
181}
182
183impl FromTopLevel for Namespace {
184 fn from_toplevel<'a>(toplevel: &'a mut TopLevel, usr: &Usr) -> Option<&'a mut Described<Self>> {
185 toplevel.namespaces.get_mut(usr)
186 }
187}
188
189impl FromTopLevel for Variable {
190 fn from_toplevel<'a>(toplevel: &'a mut TopLevel, usr: &Usr) -> Option<&'a mut Described<Self>> {
191 toplevel.variables.get_mut(usr)
192 }
193}
194
195impl FromTopLevel for Function {
196 fn from_toplevel<'a>(toplevel: &'a mut TopLevel, usr: &Usr) -> Option<&'a mut Described<Self>> {
197 toplevel.functions.get_mut(usr)
198 }
199}
200
201impl FromTopLevel for Struct {
202 fn from_toplevel<'a>(toplevel: &'a mut TopLevel, usr: &Usr) -> Option<&'a mut Described<Self>> {
203 toplevel.structs.get_mut(usr)
204 }
205}
206*/
207
208pub(crate) fn parse_compile_commands(
209 config: &Config,
210 codemap: &mut CodeMap,
211) -> Result<BTreeMap<EntityId, Entity>> {
212 let clang = Clang::new().unwrap();
213 let index = Index::new(
214 &clang, /* exclude from pch = */ false, /* print diagnostics = */ false,
215 );
216
217 debug!("Extra libclang argument: {:?}", config.extra_args);
218
219 debug!(
220 "Loading compile commands from: {:?}",
221 config.compile_commands_location
222 );
223 let database =
224 CompilationDatabase::from_directory(&config.compile_commands_location).map_err(|()| {
225 CompileCommandsLoadError {
226 path: config.compile_commands_location.clone(),
227 }
228 })?;
229
230 let toplevel_directory = std::env::current_dir().context("Cannot read current directory")?;
231
232 let mut entities = TopLevel::default();
233
234 for command in database.get_all_compile_commands().get_commands() {
235 let directory = command.get_directory();
236 trace!("Changing directory to: {:?}", directory);
237 std::env::set_current_dir(&directory)
238 .with_context(|| format!("Cannot change current directory to: {:?}", directory))?;
239
240 let filename = command.get_filename();
241
242 let file_map = codemap.add_file(
243 filename
244 .to_str()
245 .context("File is not valid UTF-8")?
246 .to_owned(),
247 std::fs::read_to_string(&filename)
248 .with_context(|| format!("Cannot readfile: {:?}", filename))?,
249 );
250
251 trace!("Parsing file: {:?}", filename);
252 // The file name is passed as an argument in the compile commands
253 let mut parser = index.parser("");
254 parser.skip_function_bodies(true);
255
256 let mut clang_arguments = command.get_arguments();
257 clang_arguments.extend_from_slice(&config.extra_args);
258 trace!("Parsing with libclang arguments: {:?}", clang_arguments);
259 parser.arguments(&clang_arguments);
260
261 parse_unit(
262 &parser
263 .parse()
264 .with_context(|| format!("Could not parse file: {:?}", filename))?,
265 &mut entities,
266 &toplevel_directory,
267 file_map.span,
268 &codemap,
269 )?;
270
271 trace!("Changing directory to: {:?}", directory);
272 std::env::set_current_dir(&toplevel_directory).with_context(|| {
273 format!(
274 "Cannot change current directory to: {:?}",
275 toplevel_directory
276 )
277 })?;
278 }
279
280 let normalized_entities = entities
281 .namespaces
282 .into_iter()
283 .map(|(usr, entity)| (EntityId(usr.0), entity.into()))
284 .chain(
285 entities
286 .variables
287 .into_iter()
288 .map(|(usr, entity)| (EntityId(usr.0), entity.into())),
289 )
290 .chain(
291 entities
292 .structs
293 .into_iter()
294 .map(|(usr, entity)| (EntityId(usr.0), entity.into())),
295 )
296 .chain(
297 entities
298 .functions
299 .into_iter()
300 .map(|(usr, entity)| (EntityId(usr.0), entity.into())),
301 )
302 .collect();
303
304 Ok(normalized_entities)
305}
306
307/*
308pub(crate) fn parse_file<T, S>(path: T, extra_args: &[S]) -> EntitiesManager
309where
310 T: Into<PathBuf>,
311 T: AsRef<Path>,
312 T: ToString,
313 S: AsRef<str>,
314 S: std::fmt::Debug,
315{
316 let mut codemap = CodeMap::new();
317 let file_map = codemap.add_file(path.to_string(), std::fs::read_to_string(&path).unwrap());
318 let file_span = file_map.span;
319
320 let clang = Clang::new().unwrap();
321 let index = Index::new(&clang, true, false);
322 let mut parser = index.parser(path);
323 parser.skip_function_bodies(true);
324
325 parser.arguments(&extra_args);
326
327 if log_enabled!(log::Level::Debug) {
328 for extra_arg in extra_args {
329 debug!("Extra libclang argument: {:?}", extra_arg);
330 }
331 }
332
333 let trans_unit = parser.parse().unwrap();
334 let mut entities = EntitiesManager::new();
335
336 parse_unit(
337 &trans_unit,
338 &mut entities,
339 &std::env::current_dir().unwrap(),
340 file_span,
341 &codemap,
342 )
343 .unwrap();
344
345 entities
346}
347*/
348
349fn parse_unit(
350 trans_unit: &TranslationUnit,
351 entities: &mut TopLevel,
352 base_dir: impl AsRef<Path>,
353 file_span: codemap::Span,
354 codemap: &CodeMap,
355) -> Result<()> {
356 trans_unit.get_entity().visit_children(|entity, _parent| {
357 if is_in_system_header(entity, &base_dir) {
358 trace!(
359 "Entity is in system header, skipping: USR = {:?}",
360 entity.get_display_name()
361 );
362 return clang::EntityVisitResult::Continue;
363 }
364
365 // TODO: wrap this callback in another function so that we can use the
366 // "?" operator instead of all these `match`es
367 let usr = match entity.get_usr() {
368 Some(usr) => usr,
369 None => return clang::EntityVisitResult::Continue,
370 };
371 trace!("Entity with USR = {:?}", usr);
372 debug!("Parsing toplevel entity: {:?}", entity);
373
374 add_entity(entity, entities, file_span, codemap)
375 });
376
377 /*
378 use codemap_diagnostic::{ColorConfig, Emitter};
379
380 let mut emitter = Emitter::stderr(ColorConfig::Auto, Some(&codemap));
381
382 for diagnostic in trans_unit.get_diagnostics().iter() {
383 let main_diag = match clang_diag_to_codemap_diag(&diagnostic, file_span) {
384 Some(diag) => diag,
385 None => continue,
386 };
387
388 let sub_diags = diagnostic
389 .get_children()
390 .into_iter()
391 .filter_map(|diagnostic| clang_diag_to_codemap_diag(&diagnostic, file_span));
392
393 let fix_it_diags = diagnostic
394 .get_fix_its()
395 .into_iter()
396 .map(|fix_it| clang_fix_it_to_codemap_diag(&fix_it, file_span));
397
398 emitter.emit(
399 &std::iter::once(main_diag)
400 .chain(sub_diags)
401 .chain(fix_it_diags)
402 .collect::<Vec<_>>(),
403 );
404 }
405 */
406
407 Ok(())
408}
409
410fn is_in_system_header(entity: clang::Entity, base_dir: impl AsRef<Path>) -> bool {
411 if entity.is_in_system_header() {
412 true
413 } else if let Some(location) = entity.get_location() {
414 if let Some(file) = location.get_file_location().file {
415 !file
416 .get_path()
417 .canonicalize()
418 .unwrap()
419 .starts_with(base_dir)
420 } else {
421 // Not defined in a file? probably shouldn't document
422 true
423 }
424 } else {
425 // Not defined anywhere? probably shouldn't document
426 true
427 }
428}
429
430// Entries encountered in the toplevel lexical context
431fn add_entity(
432 libclang_entity: clang::Entity,
433 toplevel: &mut TopLevel,
434 file_span: codemap::Span,
435 codemap: &CodeMap,
436) -> clang::EntityVisitResult {
437 if libclang_entity.get_usr().is_none() {
438 return clang::EntityVisitResult::Continue;
439 };
440
441 let kind = match ClangEntityKind::try_from(libclang_entity.get_kind()) {
442 Ok(kind) => kind,
443 Err(err) => {
444 use codemap_diagnostic::{
445 ColorConfig, Diagnostic, Emitter, Level, SpanLabel, SpanStyle,
446 };
447 let spans = if let Some(range) = libclang_entity.get_range() {
448 // TODO: add every file parsed in this translation unit to the
449 // codemap, so we can properly report errors
450 if !range.is_in_main_file() {
451 vec![]
452 } else {
453 let begin = range.get_start().get_file_location().offset as u64;
454 let end = range.get_end().get_file_location().offset as u64;
455
456 vec![SpanLabel {
457 span: file_span.subspan(begin, end),
458 label: None,
459 style: SpanStyle::Primary,
460 }]
461 }
462 } else {
463 vec![]
464 };
465
466 let diag = Diagnostic {
467 level: Level::Warning,
468 message: format!("{}", err),
469 code: None,
470 spans,
471 };
472
473 let mut emitter = Emitter::stderr(ColorConfig::Auto, Some(codemap));
474 emitter.emit(&[diag]);
475
476 return clang::EntityVisitResult::Continue;
477 }
478 };
479
480 if let Some(in_tree_entity) = toplevel.get_entity_mut(libclang_entity) {
481 // if current.has_documentation && !tree.has_documentation {
482 // append_documentation
483 // }
484 } else if libclang_entity.is_definition() {
485 // TODO: This probably means that you can't put documentation in forward declarations.
486 // TODO: Ad-hoc toplevel functions are not documented
487 //
488 // This seems restrictive, but since there can be multiple declarations but only one definition,
489 // you should probably put your documentation on the definition anyway?
490 //
491 // Also, skipping forward declarations allows us to not have to insert, then update the tree
492 // when we see the definition.
493
494 let result = match kind {
495 ClangEntityKind::Namespace => Described::<Namespace>::try_from(libclang_entity)
496 .and_then(|namespace| toplevel.insert(libclang_entity, namespace)),
497 ClangEntityKind::Variable(_) => Described::<Variable>::try_from(libclang_entity)
498 .and_then(|variable| toplevel.insert(libclang_entity, variable)),
499 ClangEntityKind::Struct(_) => Described::<Struct>::try_from(libclang_entity)
500 .and_then(|r#struct| toplevel.insert(libclang_entity, r#struct)),
501 ClangEntityKind::Function(_) => Described::<Function>::try_from(libclang_entity)
502 .and_then(|function| toplevel.insert(libclang_entity, function)),
503 };
504 // TODO: check result
505 }
506
507 if kind == ClangEntityKind::Namespace {
508 // Recurse here since namespace definitions are allowed to change between translation units.
509 ::clang::EntityVisitResult::Recurse
510 } else {
511 ::clang::EntityVisitResult::Continue
512 }
513}
514
515impl<'a, T> TryFrom<clang::Entity<'a>> for Described<T>
516where
517 T: TryFrom<clang::Entity<'a>, Error = Error>,
518{
519 type Error = Error;
520
521 fn try_from(entity: clang::Entity<'a>) -> Result<Self, Error> {
522 Ok(Described::<T> {
523 description: get_description(entity)?,
524 entity: T::try_from(entity)?,
525 })
526 }
527}
528
529impl<'a> TryFrom<clang::Entity<'a>> for Namespace {
530 type Error = Error;
531
532 fn try_from(entity: clang::Entity) -> Result<Self, Error> {
533 match entity.get_kind().try_into() {
534 Ok(ClangEntityKind::Namespace) => {}
535 _ => panic!("Trying to parse a non-variable into a variable"),
536 }
537 debug!("Parsing Namespace: {:?}", entity);
538
539 // Do not recurse here, but recurse in the main loop, since namespace
540 // definitions is allowed to change between translation units
541
542 Ok(Namespace {
543 member_namespaces: Default::default(),
544 member_variables: Default::default(),
545 member_structs: Default::default(),
546 member_functions: Default::default(),
547 })
548 }
549}
550
551impl<'a> TryFrom<clang::Entity<'a>> for Variable {
552 type Error = Error;
553
554 fn try_from(entity: clang::Entity) -> Result<Self, Error> {
555 let variable_kind;
556 match entity.get_kind().try_into() {
557 Ok(ClangEntityKind::Variable(kind)) => {
558 variable_kind = kind;
559 }
560 _ => panic!("Trying to parse a non-variable into a variable"),
561 }
562 debug!("Parsing Variable: {:?}", entity);
563
564 let r#type = entity.get_type().unwrap().get_display_name();
565 trace!("Variable has type: {:?}", r#type);
566
567 Ok(Variable {
568 r#type,
569 kind: variable_kind,
570 })
571 }
572}
573
574impl<'a> TryFrom<clang::Entity<'a>> for Struct {
575 type Error = Error;
576
577 fn try_from(entity: clang::Entity) -> Result<Self, Error> {
578 let struct_kind;
579 match entity.get_kind().try_into() {
580 Ok(ClangEntityKind::Struct(kind)) => {
581 struct_kind = kind;
582 }
583 _ => panic!("Trying to parse a non-class into a class"),
584 }
585 debug!("Parsing Struct: {:?}", entity);
586
587 let mut member_variables = BTreeMap::new();
588 let mut member_structs = BTreeMap::new();
589 let mut member_functions = BTreeMap::new();
590
591 for child in entity.get_children() {
592 trace!("Struct has child: {:?}", child);
593
594 match child.get_kind().try_into() {
595 Ok(ClangEntityKind::Variable(_)) => {
596 let child_usr = child.get_usr().ok_or_else(|| anyhow!("no usr"))?;
597 member_variables.insert(child_usr, Described::<Variable>::try_from(child)?);
598 }
599 Ok(ClangEntityKind::Struct(_)) => {
600 let child_usr: Usr = child.get_usr().ok_or_else(|| anyhow!("no usr"))?;
601 member_structs.insert(child_usr, Described::<Struct>::try_from(child)?);
602 }
603 Ok(ClangEntityKind::Function(_)) => {
604 let child_usr = child.get_usr().ok_or_else(|| anyhow!("no usr"))?;
605 member_functions.insert(child_usr, Described::<Function>::try_from(child)?);
606 }
607 _ => trace!("Skipping child"),
608 }
609 }
610
611 Ok(Struct {
612 kind: struct_kind,
613 member_functions,
614 member_structs,
615 member_variables,
616 })
617 }
618}
619
620impl<'a> TryFrom<clang::Entity<'a>> for Function {
621 type Error = Error;
622
623 fn try_from(entity: clang::Entity) -> Result<Self, Error> {
624 let function_kind;
625 match entity.get_kind().try_into() {
626 Ok(ClangEntityKind::Function(kind)) => {
627 function_kind = kind;
628 }
629 _ => panic!("Trying to parse a non-function into a function"),
630 }
631 debug!("Parsing Function: {:?}", entity);
632
633 let return_type = entity.get_result_type().unwrap().get_display_name();
634 trace!("Function has return type: {:?}", return_type);
635 let arguments = entity
636 .get_arguments()
637 .unwrap()
638 .into_iter()
639 .map(|arg| {
640 let name = arg
641 .get_display_name()
642 .unwrap_or_else(|| String::from("unnamed"));
643 let r#type = arg.get_type().unwrap().get_display_name();
644 trace!("Function has argument {:?} of type {:?}", name, r#type);
645 FunctionArgument { name, r#type }
646 })
647 .collect();
648
649 Ok(Function {
650 kind: function_kind,
651 arguments,
652 return_type,
653 })
654 }
655}
656
657fn get_description(entity: clang::Entity) -> Result<Description> {
658 let name = entity
659 .get_display_name()
660 .ok_or_else(|| anyhow!("Entity has no name: {:?}", entity))?;
661
662 // TODO: is that the best?
663 if let (Some(brief), Some(comment)) = (entity.get_comment_brief(), entity.get_comment()) {
664 Ok(Description {
665 name,
666 brief,
667 detailed: parse_comment(comment),
668 })
669 } else {
670 Ok(Description {
671 name,
672 brief: String::new(),
673 detailed: String::new(),
674 })
675 }
676}
677
678pub fn parse_comment(raw: String) -> String {
679 #[derive(Debug)]
680 enum CommentStyle {
681 // Comments of type `/**` or `/*!`
682 Starred,
683 // Comments of type `///`
684 SingleLine,
685 }
686
687 let mut chars = raw.chars();
688 let style = match &chars.as_str()[..3] {
689 "/*!" | "/**" => CommentStyle::Starred,
690 "///" => CommentStyle::SingleLine,
691 _ => panic!("Comment is empty or doesn't start with either `///`, `/**`, or `/*!`"),
692 };
693
694 chars.nth(2);
695
696 let mut result = String::new();
697
698 'parse_loop: loop {
699 let maybe_space = chars.next();
700 let mut empty_line = false;
701 match maybe_space {
702 // TODO: Warn on empty comments
703 None => break,
704 Some(' ') => {}
705 Some('\n') => {
706 empty_line = true;
707 result.push('\n');
708 }
709 Some(ch) => result.push(ch),
710 }
711
712 if !empty_line {
713 let rest = chars.as_str();
714 match rest.find('\n') {
715 None => {
716 result.push_str(rest);
717 break;
718 }
719 Some(position) => {
720 result.push_str(&rest[..=position]);
721 chars.nth(position);
722 }
723 }
724 }
725
726 // Beginning of the line
727 let first_non_ws_ch = 'ws_loop: loop {
728 let maybe_whitespace = chars.next();
729 match maybe_whitespace {
730 None => break 'parse_loop,
731 Some(ch) if ch.is_whitespace() => continue,
732 Some(ch) => break 'ws_loop ch,
733 }
734 };
735
736 match style {
737 CommentStyle::Starred if first_non_ws_ch == '*' => {
738 if &chars.as_str()[..1] == "/" {
739 break;
740 }
741 }
742 CommentStyle::Starred => result.push(first_non_ws_ch),
743 CommentStyle::SingleLine => {
744 assert!(first_non_ws_ch == '/');
745 let rest = chars.as_str();
746 if &rest[..2] == "//" {
747 chars.nth(1);
748 } else if &rest[..1] == "/" {
749 chars.nth(0);
750 } else {
751 panic!("Could not parse comment");
752 }
753 }
754 }
755 }
756
757 result
758}
759
760#[derive(Debug, Clone, Error)]
761#[error("Failed to load 'compile_commands.json' at path: {:?}", path)]
762pub(crate) struct CompileCommandsLoadError {
763 path: PathBuf,
764}
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
new file mode 100644
index 0000000..0736eee
--- /dev/null
+++ b/src/parser/mod.rs
@@ -0,0 +1 @@
pub(crate) mod clang;