summaryrefslogtreecommitdiffstats
path: root/src/parser/clang/parsing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser/clang/parsing.rs')
-rw-r--r--src/parser/clang/parsing.rs282
1 files changed, 203 insertions, 79 deletions
diff --git a/src/parser/clang/parsing.rs b/src/parser/clang/parsing.rs
index a654681..1f1691f 100644
--- a/src/parser/clang/parsing.rs
+++ b/src/parser/clang/parsing.rs
@@ -11,12 +11,15 @@ use std::collections::BTreeMap;
11use std::convert::{TryFrom, TryInto}; 11use std::convert::{TryFrom, TryInto};
12use std::path::{Path, PathBuf}; 12use std::path::{Path, PathBuf};
13 13
14// TODO: check for use of Extend instead of loop+insert
15
14#[derive(Debug, Default)] 16#[derive(Debug, Default)]
15struct TopLevel { 17struct TopLevel {
16 namespaces: BTreeMap<Usr, Described<Namespace>>, 18 namespaces: BTreeMap<Usr, Described<Namespace>>,
17 variables: BTreeMap<Usr, Described<Variable>>, 19 variables: BTreeMap<Usr, Described<Variable>>,
18 structs: BTreeMap<Usr, Described<Struct>>, 20 structs: BTreeMap<Usr, Described<Struct>>,
19 functions: BTreeMap<Usr, Described<Function>>, 21 functions: BTreeMap<Usr, Described<Function>>,
22 typedefs: BTreeMap<Usr, Described<Typedef>>,
20} 23}
21 24
22/* 25/*
@@ -82,6 +85,9 @@ impl TopLevel {
82 ClangEntityKind::Struct(_) => { 85 ClangEntityKind::Struct(_) => {
83 &mut parent.get_member_structs()?.get_mut(&usr)?.entity 86 &mut parent.get_member_structs()?.get_mut(&usr)?.entity
84 } 87 }
88 ClangEntityKind::Typedef => {
89 &mut parent.get_member_typedefs()?.get_mut(&usr)?.entity
90 }
85 }) 91 })
86 } else { 92 } else {
87 Some(match path.get_kind().try_into().ok()? { 93 Some(match path.get_kind().try_into().ok()? {
@@ -89,6 +95,7 @@ impl TopLevel {
89 ClangEntityKind::Variable(_) => &mut self.variables.get_mut(&usr)?.entity, 95 ClangEntityKind::Variable(_) => &mut self.variables.get_mut(&usr)?.entity,
90 ClangEntityKind::Struct(_) => &mut self.structs.get_mut(&usr)?.entity, 96 ClangEntityKind::Struct(_) => &mut self.structs.get_mut(&usr)?.entity,
91 ClangEntityKind::Function(_) => &mut self.functions.get_mut(&usr)?.entity, 97 ClangEntityKind::Function(_) => &mut self.functions.get_mut(&usr)?.entity,
98 ClangEntityKind::Typedef => &mut self.typedefs.get_mut(&usr)?.entity,
92 }) 99 })
93 } 100 }
94 } 101 }
@@ -121,7 +128,10 @@ impl TopLevel {
121 .insert(usr, entity); 128 .insert(usr, entity);
122 Ok(()) 129 Ok(())
123 } else { 130 } else {
124 Err(anyhow!("has parent but no parent in tree")) 131 Err(anyhow!(
132 "has parent: {:?} but no parent in tree",
133 parent_path
134 ))
125 } 135 }
126 } else { 136 } else {
127 self.insert_toplevel(usr, entity); 137 self.insert_toplevel(usr, entity);
@@ -175,6 +185,12 @@ impl TopLevelManipulation<Struct> for TopLevel {
175 } 185 }
176} 186}
177 187
188impl TopLevelManipulation<Typedef> for TopLevel {
189 fn insert_toplevel(&mut self, usr: Usr, entity: Described<Typedef>) {
190 self.typedefs.insert(usr, entity);
191 }
192}
193
178/* 194/*
179trait FromTopLevel: ClangEntity + Sized { 195trait FromTopLevel: ClangEntity + Sized {
180 fn from_toplevel<'a>(toplevel: &'a mut TopLevel, usr: &Usr) -> Option<&'a mut Described<Self>>; 196 fn from_toplevel<'a>(toplevel: &'a mut TopLevel, usr: &Usr) -> Option<&'a mut Described<Self>>;
@@ -268,7 +284,7 @@ pub(crate) fn parse_compile_commands(
268 &codemap, 284 &codemap,
269 )?; 285 )?;
270 286
271 trace!("Changing directory to: {:?}", directory); 287 trace!("Changing directory to: {:?}", toplevel_directory);
272 std::env::set_current_dir(&toplevel_directory).with_context(|| { 288 std::env::set_current_dir(&toplevel_directory).with_context(|| {
273 format!( 289 format!(
274 "Cannot change current directory to: {:?}", 290 "Cannot change current directory to: {:?}",
@@ -277,74 +293,77 @@ pub(crate) fn parse_compile_commands(
277 })?; 293 })?;
278 } 294 }
279 295
280 let normalized_entities = entities 296 Ok(entities.into())
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} 297}
306 298
307/* 299pub(crate) fn parse_file<T>(
308pub(crate) fn parse_file<T, S>(path: T, extra_args: &[S]) -> EntitiesManager 300 path: T,
301 config: &Config,
302 codemap: &mut CodeMap,
303) -> Result<BTreeMap<EntityId, Entity>>
309where 304where
310 T: Into<PathBuf>, 305 T: Into<PathBuf>,
311 T: AsRef<Path>, 306 T: AsRef<Path>,
312 T: ToString, 307 T: ToString,
313 S: AsRef<str>,
314 S: std::fmt::Debug,
315{ 308{
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(); 309 let clang = Clang::new().unwrap();
321 let index = Index::new(&clang, true, false); 310 let index = Index::new(&clang, true, false);
322 let mut parser = index.parser(path);
323 parser.skip_function_bodies(true);
324 311
325 parser.arguments(&extra_args); 312 // as provided in the command line
313 let filename = path.to_string();
314 let file_map = codemap.add_file(filename.clone(), std::fs::read_to_string(&path)?);
326 315
327 if log_enabled!(log::Level::Debug) { 316 let path = path.as_ref().canonicalize()?;
328 for extra_arg in extra_args { 317 let toplevel_directory = std::env::current_dir().context("Cannot read current directory")?;
329 debug!("Extra libclang argument: {:?}", extra_arg); 318
330 } 319 let maybe_commands = CompilationDatabase::from_directory(&config.compile_commands_location)
320 .and_then(|database| database.get_compile_commands(&path))
321 .ok();
322 let maybe_command = maybe_commands
323 .as_ref()
324 .and_then(|commands| commands.get_commands().pop());
325
326 let mut clang_arguments = maybe_command
327 .map(|command| command.get_arguments())
328 .unwrap_or_default();
329 clang_arguments.extend_from_slice(&config.extra_args);
330
331 if let Some(command) = maybe_command {
332 let directory = command.get_directory();
333 trace!("Changing directory to: {:?}", directory);
334 std::env::set_current_dir(&directory)
335 .with_context(|| format!("Cannot change current directory to: {:?}", directory))?;
331 } 336 }
332 337
333 let trans_unit = parser.parse().unwrap(); 338 let mut parser = index.parser("");
334 let mut entities = EntitiesManager::new(); 339 parser.skip_function_bodies(true);
340
341 parser.arguments(&clang_arguments);
342
343 trace!("Parsing with libclang arguments: {:?}", clang_arguments);
344
345 let mut entities = TopLevel::default();
335 346
336 parse_unit( 347 parse_unit(
337 &trans_unit, 348 &parser
349 .parse()
350 .with_context(|| format!("Could not parse file: {:?}", filename))?,
338 &mut entities, 351 &mut entities,
339 &std::env::current_dir().unwrap(), 352 &toplevel_directory,
340 file_span, 353 file_map.span,
341 &codemap, 354 &codemap,
342 ) 355 )?;
343 .unwrap();
344 356
345 entities 357 trace!("Changing directory to: {:?}", toplevel_directory);
358 std::env::set_current_dir(&toplevel_directory).with_context(|| {
359 format!(
360 "Cannot change current directory to: {:?}",
361 toplevel_directory
362 )
363 })?;
364
365 Ok(entities.into())
346} 366}
347*/
348 367
349fn parse_unit( 368fn parse_unit(
350 trans_unit: &TranslationUnit, 369 trans_unit: &TranslationUnit,
@@ -355,10 +374,7 @@ fn parse_unit(
355) -> Result<()> { 374) -> Result<()> {
356 trans_unit.get_entity().visit_children(|entity, _parent| { 375 trans_unit.get_entity().visit_children(|entity, _parent| {
357 if is_in_system_header(entity, &base_dir) { 376 if is_in_system_header(entity, &base_dir) {
358 trace!( 377 trace!("Entity is in system header, skipping: {:?}", entity);
359 "Entity is in system header, skipping: USR = {:?}",
360 entity.get_display_name()
361 );
362 return clang::EntityVisitResult::Continue; 378 return clang::EntityVisitResult::Continue;
363 } 379 }
364 380
@@ -374,12 +390,13 @@ fn parse_unit(
374 add_entity(entity, entities, file_span, codemap) 390 add_entity(entity, entities, file_span, codemap)
375 }); 391 });
376 392
377 /*
378 use codemap_diagnostic::{ColorConfig, Emitter}; 393 use codemap_diagnostic::{ColorConfig, Emitter};
379 394
380 let mut emitter = Emitter::stderr(ColorConfig::Auto, Some(&codemap)); 395 let mut emitter = Emitter::stderr(ColorConfig::Auto, Some(&codemap));
381 396
382 for diagnostic in trans_unit.get_diagnostics().iter() { 397 for diagnostic in trans_unit.get_diagnostics().iter() {
398 warn!("{}", diagnostic);
399 /*
383 let main_diag = match clang_diag_to_codemap_diag(&diagnostic, file_span) { 400 let main_diag = match clang_diag_to_codemap_diag(&diagnostic, file_span) {
384 Some(diag) => diag, 401 Some(diag) => diag,
385 None => continue, 402 None => continue,
@@ -401,8 +418,8 @@ fn parse_unit(
401 .chain(fix_it_diags) 418 .chain(fix_it_diags)
402 .collect::<Vec<_>>(), 419 .collect::<Vec<_>>(),
403 ); 420 );
421 */
404 } 422 }
405 */
406 423
407 Ok(()) 424 Ok(())
408} 425}
@@ -412,11 +429,12 @@ fn is_in_system_header(entity: clang::Entity, base_dir: impl AsRef<Path>) -> boo
412 true 429 true
413 } else if let Some(location) = entity.get_location() { 430 } else if let Some(location) = entity.get_location() {
414 if let Some(file) = location.get_file_location().file { 431 if let Some(file) = location.get_file_location().file {
415 !file 432 // !file
416 .get_path() 433 // .get_path()
417 .canonicalize() 434 // .canonicalize()
418 .unwrap() 435 // .unwrap()
419 .starts_with(base_dir) 436 // .starts_with(base_dir)
437 false
420 } else { 438 } else {
421 // Not defined in a file? probably shouldn't document 439 // Not defined in a file? probably shouldn't document
422 true 440 true
@@ -481,7 +499,8 @@ fn add_entity(
481 // if current.has_documentation && !tree.has_documentation { 499 // if current.has_documentation && !tree.has_documentation {
482 // append_documentation 500 // append_documentation
483 // } 501 // }
484 } else if libclang_entity.is_definition() { 502 } else {
503 //if libclang_entity.is_definition() {
485 // TODO: This probably means that you can't put documentation in forward declarations. 504 // TODO: This probably means that you can't put documentation in forward declarations.
486 // TODO: Ad-hoc toplevel functions are not documented 505 // TODO: Ad-hoc toplevel functions are not documented
487 // 506 //
@@ -492,19 +511,32 @@ fn add_entity(
492 // when we see the definition. 511 // when we see the definition.
493 512
494 let result = match kind { 513 let result = match kind {
495 ClangEntityKind::Namespace => Described::<Namespace>::try_from(libclang_entity) 514 ClangEntityKind::Namespace => {
496 .and_then(|namespace| toplevel.insert(libclang_entity, namespace)), 515 if libclang_entity.get_name().is_some() {
516 Described::<Namespace>::try_from(libclang_entity)
517 .and_then(|namespace| toplevel.insert(libclang_entity, namespace))
518 } else {
519 Ok(())
520 }
521 }
497 ClangEntityKind::Variable(_) => Described::<Variable>::try_from(libclang_entity) 522 ClangEntityKind::Variable(_) => Described::<Variable>::try_from(libclang_entity)
498 .and_then(|variable| toplevel.insert(libclang_entity, variable)), 523 .and_then(|variable| toplevel.insert(libclang_entity, variable)),
499 ClangEntityKind::Struct(_) => Described::<Struct>::try_from(libclang_entity) 524 ClangEntityKind::Struct(_) => Described::<Struct>::try_from(libclang_entity)
500 .and_then(|r#struct| toplevel.insert(libclang_entity, r#struct)), 525 .and_then(|r#struct| toplevel.insert(libclang_entity, r#struct)),
501 ClangEntityKind::Function(_) => Described::<Function>::try_from(libclang_entity) 526 ClangEntityKind::Function(_) => Described::<Function>::try_from(libclang_entity)
502 .and_then(|function| toplevel.insert(libclang_entity, function)), 527 .and_then(|function| toplevel.insert(libclang_entity, function)),
528 ClangEntityKind::Typedef => Described::<Typedef>::try_from(libclang_entity)
529 .and_then(|function| toplevel.insert(libclang_entity, function)),
503 }; 530 };
504 // TODO: check result 531 // TODO: check result
532
533 if let Err(err) = result {
534 error!("{}: {:?}", err, libclang_entity);
535 return ::clang::EntityVisitResult::Continue;
536 }
505 } 537 }
506 538
507 if kind == ClangEntityKind::Namespace { 539 if kind == ClangEntityKind::Namespace && libclang_entity.get_name().is_some() {
508 // Recurse here since namespace definitions are allowed to change between translation units. 540 // Recurse here since namespace definitions are allowed to change between translation units.
509 ::clang::EntityVisitResult::Recurse 541 ::clang::EntityVisitResult::Recurse
510 } else { 542 } else {
@@ -512,6 +544,40 @@ fn add_entity(
512 } 544 }
513} 545}
514 546
547impl From<TopLevel> for BTreeMap<EntityId, Entity> {
548 fn from(toplevel: TopLevel) -> Self {
549 toplevel
550 .namespaces
551 .into_iter()
552 .map(|(usr, entity)| (EntityId(usr.0), entity.into()))
553 .chain(
554 toplevel
555 .variables
556 .into_iter()
557 .map(|(usr, entity)| (EntityId(usr.0), entity.into())),
558 )
559 .chain(
560 toplevel
561 .structs
562 .into_iter()
563 .map(|(usr, entity)| (EntityId(usr.0), entity.into())),
564 )
565 .chain(
566 toplevel
567 .functions
568 .into_iter()
569 .map(|(usr, entity)| (EntityId(usr.0), entity.into())),
570 )
571 .chain(
572 toplevel
573 .typedefs
574 .into_iter()
575 .map(|(usr, entity)| (EntityId(usr.0), entity.into())),
576 )
577 .collect()
578 }
579}
580
515impl<'a, T> TryFrom<clang::Entity<'a>> for Described<T> 581impl<'a, T> TryFrom<clang::Entity<'a>> for Described<T>
516where 582where
517 T: TryFrom<clang::Entity<'a>, Error = Error>, 583 T: TryFrom<clang::Entity<'a>, Error = Error>,
@@ -544,6 +610,7 @@ impl<'a> TryFrom<clang::Entity<'a>> for Namespace {
544 member_variables: Default::default(), 610 member_variables: Default::default(),
545 member_structs: Default::default(), 611 member_structs: Default::default(),
546 member_functions: Default::default(), 612 member_functions: Default::default(),
613 member_typedefs: Default::default(),
547 }) 614 })
548 } 615 }
549} 616}
@@ -580,31 +647,65 @@ impl<'a> TryFrom<clang::Entity<'a>> for Struct {
580 Ok(ClangEntityKind::Struct(kind)) => { 647 Ok(ClangEntityKind::Struct(kind)) => {
581 struct_kind = kind; 648 struct_kind = kind;
582 } 649 }
583 _ => panic!("Trying to parse a non-class into a class"), 650 _ => panic!("Trying to parse a non-struct into a struct"),
584 } 651 }
585 debug!("Parsing Struct: {:?}", entity); 652 debug!("Parsing Struct: {:?}", entity);
586 653
587 let mut member_variables = BTreeMap::new(); 654 let mut member_variables = BTreeMap::new();
588 let mut member_structs = BTreeMap::new(); 655 let mut member_structs = BTreeMap::new();
589 let mut member_functions = BTreeMap::new(); 656 let mut member_functions = BTreeMap::new();
657 let mut member_typedefs = BTreeMap::new();
590 658
591 for child in entity.get_children() { 659 for child in entity.get_children() {
592 trace!("Struct has child: {:?}", child); 660 trace!("Struct has child: {:?}", child);
593 661
594 match child.get_kind().try_into() { 662 let kind = child.get_kind();
595 Ok(ClangEntityKind::Variable(_)) => { 663
596 let child_usr = child.get_usr().ok_or_else(|| anyhow!("no usr"))?; 664 match kind {
597 member_variables.insert(child_usr, Described::<Variable>::try_from(child)?); 665 ::clang::EntityKind::AccessSpecifier | ::clang::EntityKind::BaseSpecifier => {
666 continue
598 } 667 }
599 Ok(ClangEntityKind::Struct(_)) => { 668 _ => {}
600 let child_usr: Usr = child.get_usr().ok_or_else(|| anyhow!("no usr"))?; 669 }
601 member_structs.insert(child_usr, Described::<Struct>::try_from(child)?); 670
671 let mut parse_child = || -> Result<()> {
672 match kind.try_into() {
673 Ok(ClangEntityKind::Variable(_)) => {
674 let child_usr = child
675 .get_usr()
676 .ok_or_else(|| anyhow!("no usr for: {:?}", child))?;
677 member_variables.insert(child_usr, Described::<Variable>::try_from(child)?);
678 }
679 Ok(ClangEntityKind::Struct(_)) => {
680 let child_usr: Usr = child
681 .get_usr()
682 .ok_or_else(|| anyhow!("no usr for: {:?}", child))?;
683 member_structs.insert(child_usr, Described::<Struct>::try_from(child)?);
684 }
685 Ok(ClangEntityKind::Function(_)) => {
686 let child_usr = child
687 .get_usr()
688 .ok_or_else(|| anyhow!("no usr for: {:?}", child))?;
689 member_functions.insert(child_usr, Described::<Function>::try_from(child)?);
690 }
691 Ok(ClangEntityKind::Typedef) => {
692 let child_usr = child
693 .get_usr()
694 .ok_or_else(|| anyhow!("no usr for: {:?}", child))?;
695 member_typedefs.insert(child_usr, Described::<Typedef>::try_from(child)?);
696 }
697 Ok(other) => warn!("Unsupported child of struct {:?}: {:?}", other, child),
698 Err(err) => info!("Error while parsing entity {:?}: {}", child, err),
602 } 699 }
603 Ok(ClangEntityKind::Function(_)) => { 700
604 let child_usr = child.get_usr().ok_or_else(|| anyhow!("no usr"))?; 701 Ok(())
605 member_functions.insert(child_usr, Described::<Function>::try_from(child)?); 702 };
703
704 match parse_child() {
705 Ok(()) => {}
706 Err(err) => {
707 warn!("Error while parsing child {:?}: {}", child, err);
606 } 708 }
607 _ => trace!("Skipping child"),
608 } 709 }
609 } 710 }
610 711
@@ -613,6 +714,7 @@ impl<'a> TryFrom<clang::Entity<'a>> for Struct {
613 member_functions, 714 member_functions,
614 member_structs, 715 member_structs,
615 member_variables, 716 member_variables,
717 member_typedefs,
616 }) 718 })
617 } 719 }
618} 720}
@@ -634,7 +736,9 @@ impl<'a> TryFrom<clang::Entity<'a>> for Function {
634 trace!("Function has return type: {:?}", return_type); 736 trace!("Function has return type: {:?}", return_type);
635 let arguments = entity 737 let arguments = entity
636 .get_arguments() 738 .get_arguments()
637 .unwrap() 739 // TODO: this seems weird, but it fixes a None for a function that takes only a
740 // variadic argument from its own template declaration.
741 .unwrap_or_else(|| vec![])
638 .into_iter() 742 .into_iter()
639 .map(|arg| { 743 .map(|arg| {
640 let name = arg 744 let name = arg
@@ -654,6 +758,26 @@ impl<'a> TryFrom<clang::Entity<'a>> for Function {
654 } 758 }
655} 759}
656 760
761impl<'a> TryFrom<clang::Entity<'a>> for Typedef {
762 type Error = Error;
763
764 fn try_from(entity: clang::Entity) -> Result<Self, Error> {
765 match entity.get_kind().try_into() {
766 Ok(ClangEntityKind::Typedef) => {}
767 _ => panic!("Trying to parse a non-typedef into a typedef"),
768 }
769 debug!("Parsing typedef: {:?}", entity);
770
771 // TODO: unwrap (and unwrap in other similar places too)
772 let referee = entity
773 .get_typedef_underlying_type()
774 .ok_or_else(|| anyhow!("No underlying type"))?
775 .get_display_name();
776
777 Ok(Typedef { referee })
778 }
779}
780
657fn get_description(entity: clang::Entity) -> Result<Description> { 781fn get_description(entity: clang::Entity) -> Result<Description> {
658 let name = entity 782 let name = entity
659 .get_display_name() 783 .get_display_name()