summaryrefslogtreecommitdiffstats
path: root/src/parsing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parsing.rs')
-rw-r--r--src/parsing.rs482
1 files changed, 0 insertions, 482 deletions
diff --git a/src/parsing.rs b/src/parsing.rs
deleted file mode 100644
index d7aaa49..0000000
--- a/src/parsing.rs
+++ /dev/null
@@ -1,482 +0,0 @@
1use crate::entities::*;
2
3use clang::{Clang, Index};
4use codemap::CodeMap;
5
6use std::collections::HashMap;
7use std::path::{Path, PathBuf};
8
9pub(crate) fn parse_file<T, S>(path: T, extra_args: &[S]) -> EntitiesManager
10where
11 T: Into<PathBuf>,
12 T: AsRef<Path>,
13 T: ToString,
14 S: AsRef<str>,
15 S: std::fmt::Debug,
16{
17 let mut codemap = CodeMap::new();
18 let file_map = codemap.add_file(path.to_string(), std::fs::read_to_string(&path).unwrap());
19 let file_span = file_map.span;
20
21 let clang = Clang::new().unwrap();
22 let index = Index::new(&clang, true, false);
23 let mut parser = index.parser(path);
24 parser.skip_function_bodies(true);
25
26 parser.arguments(&extra_args);
27
28 if log_enabled!(log::Level::Debug) {
29 for extra_arg in extra_args {
30 debug!("Extra LibClang argument: {:?}", extra_arg);
31 }
32 }
33
34 let trans_unit = parser.parse().unwrap();
35
36 let mut manager = EntitiesManager::new();
37
38 trans_unit.get_entity().visit_children(|entity, _parent| {
39 if entity.is_in_system_header() {
40 trace!(
41 "Entity is in system header, skipping: USR = {:?}",
42 entity.get_display_name()
43 );
44 return clang::EntityVisitResult::Continue;
45 }
46
47 // TODO: wrap this callback in another function so that we can use the
48 // "?" operator instead of all these `match`es
49 let usr = match entity.get_usr() {
50 Some(usr) => usr,
51 None => return clang::EntityVisitResult::Continue,
52 };
53 trace!("Entity with USR = {:?}", usr);
54
55 let name = match entity.get_name() {
56 Some(name) => name,
57 None => return clang::EntityVisitResult::Continue,
58 };
59
60 debug!("Parsing toplevel entity with name = {:?}", name);
61
62 let entity = entity.into_poseidoc_entity(&mut manager);
63
64 manager.insert_toplevel(usr.into(), entity);
65
66 clang::EntityVisitResult::Continue
67 });
68
69 use codemap_diagnostic::{ColorConfig, Emitter};
70
71 let mut emitter = Emitter::stderr(ColorConfig::Auto, Some(&codemap));
72
73 for diagnostic in trans_unit.get_diagnostics().iter() {
74 let main_diag = match clang_diag_to_codemap_diag(&diagnostic, file_span) {
75 Some(diag) => diag,
76 None => continue,
77 };
78
79 let sub_diags = diagnostic
80 .get_children()
81 .into_iter()
82 .filter_map(|diagnostic| clang_diag_to_codemap_diag(&diagnostic, file_span));
83
84 let fix_it_diags = diagnostic
85 .get_fix_its()
86 .into_iter()
87 .map(|fix_it| clang_fix_it_to_codemap_diag(&fix_it, file_span));
88
89 emitter.emit(
90 &std::iter::once(main_diag)
91 .chain(sub_diags)
92 .chain(fix_it_diags)
93 .collect::<Vec<_>>(),
94 );
95 }
96
97 manager
98}
99
100fn clang_diag_to_codemap_diag(
101 diagnostic: &clang::diagnostic::Diagnostic,
102 file_span: codemap::Span,
103) -> Option<codemap_diagnostic::Diagnostic> {
104 use codemap_diagnostic::{Diagnostic, Level, SpanLabel, SpanStyle};
105
106 let ranges = diagnostic.get_ranges();
107
108 let (begin, end) = if ranges.is_empty() {
109 let location = diagnostic.get_location();
110
111 (
112 location.get_file_location().offset as u64,
113 location.get_file_location().offset as u64,
114 )
115 } else {
116 let range = diagnostic.get_ranges()[0];
117 if !range.is_in_main_file() {
118 warn!("Skipping diagnostic: {:?}", diagnostic);
119 return None;
120 }
121 (
122 range.get_start().get_file_location().offset as u64,
123 range.get_end().get_file_location().offset as u64,
124 )
125 };
126
127 let label = SpanLabel {
128 span: file_span.subspan(begin, end),
129 label: None,
130 style: SpanStyle::Primary,
131 };
132
133 Some(Diagnostic {
134 level: match diagnostic.get_severity() {
135 clang::diagnostic::Severity::Ignored => return None,
136 clang::diagnostic::Severity::Note => Level::Note,
137 clang::diagnostic::Severity::Warning => Level::Warning,
138 clang::diagnostic::Severity::Error => Level::Error,
139 clang::diagnostic::Severity::Fatal => Level::Error,
140 },
141 message: diagnostic.get_text(),
142 //code: Some("-Werror=documentation".to_string()),
143 code: None,
144 spans: vec![label],
145 })
146}
147
148fn clang_fix_it_to_codemap_diag(
149 fix_it: &clang::diagnostic::FixIt,
150 file_span: codemap::Span,
151) -> codemap_diagnostic::Diagnostic {
152 use clang::diagnostic::FixIt;
153 use codemap_diagnostic::{Diagnostic, Level, SpanLabel, SpanStyle};
154
155 let label = match fix_it {
156 FixIt::Deletion(range) => {
157 let begin = range.get_start().get_file_location().offset as u64;
158 let end = range.get_end().get_file_location().offset as u64;
159
160 SpanLabel {
161 span: file_span.subspan(begin, end),
162 label: Some(String::from("Try deleting this")),
163 style: SpanStyle::Primary,
164 }
165 }
166 FixIt::Insertion(range, text) => {
167 let location = range.get_file_location().offset as u64;
168
169 SpanLabel {
170 span: file_span.subspan(location, location),
171 label: Some(format!("Try insterting {:?}", text)),
172 style: SpanStyle::Primary,
173 }
174 }
175 FixIt::Replacement(range, text) => {
176 let begin = range.get_start().get_file_location().offset as u64;
177 let end = range.get_end().get_file_location().offset as u64;
178
179 SpanLabel {
180 span: file_span.subspan(begin, end),
181 label: Some(format!("Try replacing this with {:?}", text)),
182 style: SpanStyle::Primary,
183 }
184 }
185 };
186
187 Diagnostic {
188 level: Level::Help,
189 message: String::from("Possible fix"),
190 //code: Some("-Werror=documentation".to_string()),
191 code: None,
192 spans: vec![label],
193 }
194}
195
196pub(crate) struct Comment(String);
197
198impl Comment {
199 // TODO: weirdness with emojis and line returns?
200 pub fn from_raw(raw: String) -> Self {
201 #[derive(Debug)]
202 enum CommentStyle {
203 // Comments of type `/**` or `/*!`
204 Starred,
205 // Comments of type `///`
206 SingleLine,
207 }
208
209 let mut chars = raw.chars();
210 let style = match &chars.as_str()[..3] {
211 "/*!" | "/**" => CommentStyle::Starred,
212 "///" => CommentStyle::SingleLine,
213 _ => panic!("Comment is empty or doesn't start with either `///`, `/**`, or `/*!`"),
214 };
215
216 chars.nth(2);
217
218 let mut result = String::new();
219
220 'parse_loop: loop {
221 let maybe_space = chars.next();
222 let mut empty_line = false;
223 match maybe_space {
224 // TODO: Warn on empty comments
225 None => break,
226 Some(' ') => {}
227 Some('\n') => {
228 empty_line = true;
229 result.push('\n');
230 }
231 Some(ch) => result.push(ch),
232 }
233
234 if !empty_line {
235 let rest = chars.as_str();
236 match rest.find('\n') {
237 None => {
238 result.push_str(rest);
239 break;
240 }
241 Some(position) => {
242 result.push_str(&rest[..=position]);
243 chars.nth(position);
244 }
245 }
246 }
247
248 // Beginning of the line
249 let first_non_ws_ch = 'ws_loop: loop {
250 let maybe_whitespace = chars.next();
251 match maybe_whitespace {
252 None => break 'parse_loop,
253 Some(ch) if ch.is_whitespace() => continue,
254 Some(ch) => break 'ws_loop ch,
255 }
256 };
257
258 match style {
259 CommentStyle::Starred if first_non_ws_ch == '*' => {
260 if &chars.as_str()[..1] == "/" {
261 break;
262 }
263 }
264 CommentStyle::Starred => result.push(first_non_ws_ch),
265 CommentStyle::SingleLine => {
266 assert!(first_non_ws_ch == '/');
267 let rest = chars.as_str();
268 if &rest[..2] == "//" {
269 chars.nth(1);
270 } else if &rest[..1] == "/" {
271 chars.nth(0);
272 } else {
273 panic!("Could not parse comment");
274 }
275 }
276 }
277 }
278
279 Self(result)
280 }
281}
282
283impl From<clang::Usr> for Usr {
284 fn from(usr: clang::Usr) -> Self {
285 Self(usr.0)
286 }
287}
288
289trait FromClangEntity {
290 /// Is responsible for inserting its documentation into the entities manager
291 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self;
292}
293
294trait IntoPoseidocEntity<T> {
295 /// Useful for the `clang_entity.into_poseidoc_entity(&mut manager)` syntax. Implement
296 /// `FromClangEntity` instead.
297 fn into_poseidoc_entity(self, manager: &mut EntitiesManager) -> T;
298}
299
300impl<T> IntoPoseidocEntity<T> for clang::Entity<'_>
301where
302 T: FromClangEntity,
303{
304 fn into_poseidoc_entity(self, manager: &mut EntitiesManager) -> T {
305 T::from_clang_entity(self, manager)
306 }
307}
308
309fn get_description(entity: &clang::Entity) -> Description {
310 let name = entity.get_display_name().unwrap();
311
312 if let (Some(brief), Some(comment)) = (entity.get_comment_brief(), entity.get_comment()) {
313 Description {
314 name,
315 brief,
316 detailed: Comment::from_raw(comment).0,
317 }
318 } else {
319 Description::with_name(name)
320 }
321}
322
323impl FromClangEntity for Box<DynEntity> {
324 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
325 use clang::EntityKind;
326
327 match entity.get_kind() {
328 EntityKind::Namespace => Box::new(NameSpace::from_clang_entity(entity, manager)),
329 EntityKind::FieldDecl | EntityKind::VarDecl => {
330 Box::new(Variable::from_clang_entity(entity, manager))
331 }
332 EntityKind::FunctionDecl | EntityKind::Method | EntityKind::FunctionTemplate => {
333 Box::new(Function::from_clang_entity(entity, manager))
334 }
335 EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::ClassTemplate => {
336 Box::new(Class::from_clang_entity(entity, manager))
337 }
338 _ => panic!("Unsupported entity: {:?}", entity),
339 }
340 }
341}
342
343impl FromClangEntity for NameSpace {
344 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
345 match entity.get_kind() {
346 clang::EntityKind::Namespace => {}
347 _ => panic!("Trying to parse a non-variable into a variable"),
348 }
349 debug!("Parsing NameSpace");
350
351 let members = entity
352 .get_children()
353 .into_iter()
354 .map(|child| {
355 let usr = child.get_usr().unwrap().into();
356 trace!("Namespace has member: {:?}", usr);
357 (usr, child.into_poseidoc_entity(manager))
358 })
359 .collect();
360
361 manager.insert(entity.get_usr().unwrap().into(), get_description(&entity));
362
363 NameSpace { members }
364 }
365}
366
367impl FromClangEntity for NameSpaceChild {
368 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
369 use clang::EntityKind;
370
371 match entity.get_kind() {
372 EntityKind::Namespace => {
373 NameSpaceChild::NameSpace(NameSpace::from_clang_entity(entity, manager))
374 }
375 EntityKind::FieldDecl | EntityKind::VarDecl => {
376 NameSpaceChild::Variable(Variable::from_clang_entity(entity, manager))
377 }
378 EntityKind::FunctionDecl | EntityKind::Method | EntityKind::FunctionTemplate => {
379 NameSpaceChild::Function(Function::from_clang_entity(entity, manager))
380 }
381 EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::ClassTemplate => {
382 NameSpaceChild::Class(Class::from_clang_entity(entity, manager))
383 }
384 _ => panic!("Unsupported entity: {:?}", entity),
385 }
386 }
387}
388
389impl FromClangEntity for Variable {
390 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
391 match entity.get_kind() {
392 clang::EntityKind::VarDecl | clang::EntityKind::FieldDecl => {}
393 _ => panic!("Trying to parse a non-variable into a variable"),
394 }
395 debug!("Parsing Variable");
396
397 let r#type = entity.get_type().unwrap().get_display_name();
398 trace!("Variable has type: {:?}", r#type);
399
400 manager.insert(entity.get_usr().unwrap().into(), get_description(&entity));
401
402 Variable { r#type }
403 }
404}
405
406impl FromClangEntity for Function {
407 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
408 match entity.get_kind() {
409 clang::EntityKind::Method
410 | clang::EntityKind::Constructor
411 | clang::EntityKind::FunctionDecl
412 | clang::EntityKind::FunctionTemplate => {}
413 _ => panic!("Trying to parse a non-function into a function"),
414 }
415 debug!("Parsing Function");
416
417 let return_type = entity.get_result_type().unwrap().get_display_name();
418 trace!("Function has return type: {:?}", return_type);
419 let arguments = entity
420 .get_arguments()
421 .unwrap()
422 .into_iter()
423 .map(|arg| {
424 let name = arg.get_display_name().unwrap();
425 let r#type = arg.get_type().unwrap().get_display_name();
426 trace!("Function has argument {:?} of type {:?}", name, r#type);
427 FunctionArgument { name, r#type }
428 })
429 .collect();
430
431 manager.insert(entity.get_usr().unwrap().into(), get_description(&entity));
432
433 Function {
434 arguments,
435 return_type,
436 }
437 }
438}
439
440impl FromClangEntity for Class {
441 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
442 match entity.get_kind() {
443 clang::EntityKind::ClassDecl
444 | clang::EntityKind::StructDecl
445 | clang::EntityKind::ClassTemplate => {}
446 _ => panic!("Trying to parse a non-class into a class"),
447 }
448 debug!("Parsing Class");
449
450 let mut member_types = Vec::new();
451 let mut member_functions = HashMap::new();
452 let mut member_variables = HashMap::new();
453
454 for child in entity.get_children() {
455 trace!("Class has child: {:?}", child);
456
457 let child_usr = child.get_usr().unwrap().into();
458
459 use clang::EntityKind;
460 match child.get_kind() {
461 EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::TypeAliasDecl => {
462 member_types.push(child_usr);
463 }
464 EntityKind::Method | EntityKind::Constructor | EntityKind::FunctionDecl => {
465 member_functions.insert(child_usr, child.into_poseidoc_entity(manager));
466 }
467 EntityKind::FieldDecl => {
468 member_variables.insert(child_usr, child.into_poseidoc_entity(manager));
469 }
470 _ => trace!("Skipping child"),
471 }
472 }
473
474 manager.insert(entity.get_usr().unwrap().into(), get_description(&entity));
475
476 Class {
477 //member_types,
478 member_functions,
479 member_variables,
480 }
481 }
482}