diff options
Diffstat (limited to 'src/parsing.rs')
-rw-r--r-- | src/parsing.rs | 482 |
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 @@ | |||
1 | use crate::entities::*; | ||
2 | |||
3 | use clang::{Clang, Index}; | ||
4 | use codemap::CodeMap; | ||
5 | |||
6 | use std::collections::HashMap; | ||
7 | use std::path::{Path, PathBuf}; | ||
8 | |||
9 | pub(crate) fn parse_file<T, S>(path: T, extra_args: &[S]) -> EntitiesManager | ||
10 | where | ||
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 | |||
100 | fn 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 | |||
148 | fn 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 | |||
196 | pub(crate) struct Comment(String); | ||
197 | |||
198 | impl 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 | |||
283 | impl From<clang::Usr> for Usr { | ||
284 | fn from(usr: clang::Usr) -> Self { | ||
285 | Self(usr.0) | ||
286 | } | ||
287 | } | ||
288 | |||
289 | trait 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 | |||
294 | trait 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 | |||
300 | impl<T> IntoPoseidocEntity<T> for clang::Entity<'_> | ||
301 | where | ||
302 | T: FromClangEntity, | ||
303 | { | ||
304 | fn into_poseidoc_entity(self, manager: &mut EntitiesManager) -> T { | ||
305 | T::from_clang_entity(self, manager) | ||
306 | } | ||
307 | } | ||
308 | |||
309 | fn 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 | |||
323 | impl 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 | |||
343 | impl 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 | |||
367 | impl 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 | |||
389 | impl 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 | |||
406 | impl 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 | |||
440 | impl 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 | } | ||