summaryrefslogtreecommitdiffstats
path: root/src/parsing/clang/parsing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parsing/clang/parsing.rs')
-rw-r--r--src/parsing/clang/parsing.rs748
1 files changed, 748 insertions, 0 deletions
diff --git a/src/parsing/clang/parsing.rs b/src/parsing/clang/parsing.rs
new file mode 100644
index 0000000..5359253
--- /dev/null
+++ b/src/parsing/clang/parsing.rs
@@ -0,0 +1,748 @@
1use super::config::Config;
2use super::entities::*;
3use crate::types::Entity;
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<String, 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)| (usr.0, entity.into()))
284 .chain(entities.variables.into_iter().map(|(usr, entity)| (usr.0, entity.into())))
285 .chain(entities.structs.into_iter().map(|(usr, entity)| (usr.0, entity.into())))
286 .chain(entities.functions.into_iter().map(|(usr, entity)| (usr.0, entity.into())))
287 .collect();
288
289 Ok(normalized_entities)
290}
291
292/*
293pub(crate) fn parse_file<T, S>(path: T, extra_args: &[S]) -> EntitiesManager
294where
295 T: Into<PathBuf>,
296 T: AsRef<Path>,
297 T: ToString,
298 S: AsRef<str>,
299 S: std::fmt::Debug,
300{
301 let mut codemap = CodeMap::new();
302 let file_map = codemap.add_file(path.to_string(), std::fs::read_to_string(&path).unwrap());
303 let file_span = file_map.span;
304
305 let clang = Clang::new().unwrap();
306 let index = Index::new(&clang, true, false);
307 let mut parser = index.parser(path);
308 parser.skip_function_bodies(true);
309
310 parser.arguments(&extra_args);
311
312 if log_enabled!(log::Level::Debug) {
313 for extra_arg in extra_args {
314 debug!("Extra libclang argument: {:?}", extra_arg);
315 }
316 }
317
318 let trans_unit = parser.parse().unwrap();
319 let mut entities = EntitiesManager::new();
320
321 parse_unit(
322 &trans_unit,
323 &mut entities,
324 &std::env::current_dir().unwrap(),
325 file_span,
326 &codemap,
327 )
328 .unwrap();
329
330 entities
331}
332*/
333
334fn parse_unit(
335 trans_unit: &TranslationUnit,
336 entities: &mut TopLevel,
337 base_dir: impl AsRef<Path>,
338 file_span: codemap::Span,
339 codemap: &CodeMap,
340) -> Result<()> {
341 trans_unit.get_entity().visit_children(|entity, _parent| {
342 if is_in_system_header(entity, &base_dir) {
343 trace!(
344 "Entity is in system header, skipping: USR = {:?}",
345 entity.get_display_name()
346 );
347 return clang::EntityVisitResult::Continue;
348 }
349
350 // TODO: wrap this callback in another function so that we can use the
351 // "?" operator instead of all these `match`es
352 let usr = match entity.get_usr() {
353 Some(usr) => usr,
354 None => return clang::EntityVisitResult::Continue,
355 };
356 trace!("Entity with USR = {:?}", usr);
357 debug!("Parsing toplevel entity: {:?}", entity);
358
359 add_entity(entity, entities, file_span, codemap)
360 });
361
362 /*
363 use codemap_diagnostic::{ColorConfig, Emitter};
364
365 let mut emitter = Emitter::stderr(ColorConfig::Auto, Some(&codemap));
366
367 for diagnostic in trans_unit.get_diagnostics().iter() {
368 let main_diag = match clang_diag_to_codemap_diag(&diagnostic, file_span) {
369 Some(diag) => diag,
370 None => continue,
371 };
372
373 let sub_diags = diagnostic
374 .get_children()
375 .into_iter()
376 .filter_map(|diagnostic| clang_diag_to_codemap_diag(&diagnostic, file_span));
377
378 let fix_it_diags = diagnostic
379 .get_fix_its()
380 .into_iter()
381 .map(|fix_it| clang_fix_it_to_codemap_diag(&fix_it, file_span));
382
383 emitter.emit(
384 &std::iter::once(main_diag)
385 .chain(sub_diags)
386 .chain(fix_it_diags)
387 .collect::<Vec<_>>(),
388 );
389 }
390 */
391
392 Ok(())
393}
394
395fn is_in_system_header(entity: clang::Entity, base_dir: impl AsRef<Path>) -> bool {
396 if entity.is_in_system_header() {
397 true
398 } else if let Some(location) = entity.get_location() {
399 if let Some(file) = location.get_file_location().file {
400 !file
401 .get_path()
402 .canonicalize()
403 .unwrap()
404 .starts_with(base_dir)
405 } else {
406 // Not defined in a file? probably shouldn't document
407 true
408 }
409 } else {
410 // Not defined anywhere? probably shouldn't document
411 true
412 }
413}
414
415// Entries encountered in the toplevel lexical context
416fn add_entity(
417 libclang_entity: clang::Entity,
418 toplevel: &mut TopLevel,
419 file_span: codemap::Span,
420 codemap: &CodeMap,
421) -> clang::EntityVisitResult {
422 if libclang_entity.get_usr().is_none() {
423 return clang::EntityVisitResult::Continue;
424 };
425
426 let kind = match ClangEntityKind::try_from(libclang_entity.get_kind()) {
427 Ok(kind) => kind,
428 Err(err) => {
429 use codemap_diagnostic::{
430 ColorConfig, Diagnostic, Emitter, Level, SpanLabel, SpanStyle,
431 };
432 let spans = if let Some(range) = libclang_entity.get_range() {
433 // TODO: add every file parsed in this translation unit to the
434 // codemap, so we can properly report errors
435 if !range.is_in_main_file() {
436 vec![]
437 } else {
438 let begin = range.get_start().get_file_location().offset as u64;
439 let end = range.get_end().get_file_location().offset as u64;
440
441 vec![SpanLabel {
442 span: file_span.subspan(begin, end),
443 label: None,
444 style: SpanStyle::Primary,
445 }]
446 }
447 } else {
448 vec![]
449 };
450
451 let diag = Diagnostic {
452 level: Level::Warning,
453 message: format!("{}", err),
454 code: None,
455 spans,
456 };
457
458 let mut emitter = Emitter::stderr(ColorConfig::Auto, Some(codemap));
459 emitter.emit(&[diag]);
460
461 return clang::EntityVisitResult::Continue;
462 }
463 };
464
465 if let Some(in_tree_entity) = toplevel.get_entity_mut(libclang_entity) {
466 // if current.has_documentation && !tree.has_documentation {
467 // append_documentation
468 // }
469 } else if libclang_entity.is_definition() {
470 // TODO: This probably means that you can't put documentation in forward declarations.
471 //
472 // This seems restrictive, but since there can be multiple declarations but only one definition,
473 // you should probably put your documentation on the definition anyway?
474 //
475 // Also, skipping forward declarations allows us to not have to insert, then update the tree
476 // when we see the definition.
477
478 let result = match kind {
479 ClangEntityKind::Namespace => Described::<Namespace>::try_from(libclang_entity)
480 .and_then(|namespace| toplevel.insert(libclang_entity, namespace)),
481 ClangEntityKind::Variable(_) => Described::<Variable>::try_from(libclang_entity)
482 .and_then(|variable| toplevel.insert(libclang_entity, variable)),
483 ClangEntityKind::Struct(_) => Described::<Struct>::try_from(libclang_entity)
484 .and_then(|r#struct| toplevel.insert(libclang_entity, r#struct)),
485 ClangEntityKind::Function(_) => Described::<Function>::try_from(libclang_entity)
486 .and_then(|function| toplevel.insert(libclang_entity, function)),
487 };
488 // TODO: check result
489 }
490
491 if kind == ClangEntityKind::Namespace {
492 // Recurse here since namespace definitions are allowed to change between translation units.
493 ::clang::EntityVisitResult::Recurse
494 } else {
495 ::clang::EntityVisitResult::Continue
496 }
497}
498
499impl<'a, T> TryFrom<clang::Entity<'a>> for Described<T>
500where
501 T: TryFrom<clang::Entity<'a>, Error = Error>,
502{
503 type Error = Error;
504
505 fn try_from(entity: clang::Entity<'a>) -> Result<Self, Error> {
506 Ok(Described::<T> {
507 description: get_description(entity)?,
508 entity: T::try_from(entity)?,
509 })
510 }
511}
512
513impl<'a> TryFrom<clang::Entity<'a>> for Namespace {
514 type Error = Error;
515
516 fn try_from(entity: clang::Entity) -> Result<Self, Error> {
517 match entity.get_kind().try_into() {
518 Ok(ClangEntityKind::Namespace) => {}
519 _ => panic!("Trying to parse a non-variable into a variable"),
520 }
521 debug!("Parsing Namespace: {:?}", entity);
522
523 // Do not recurse here, but recurse in the main loop, since namespace
524 // definitions is allowed to change between translation units
525
526 Ok(Namespace {
527 member_namespaces: Default::default(),
528 member_variables: Default::default(),
529 member_structs: Default::default(),
530 member_functions: Default::default(),
531 })
532 }
533}
534
535impl<'a> TryFrom<clang::Entity<'a>> for Variable {
536 type Error = Error;
537
538 fn try_from(entity: clang::Entity) -> Result<Self, Error> {
539 let variable_kind;
540 match entity.get_kind().try_into() {
541 Ok(ClangEntityKind::Variable(kind)) => {
542 variable_kind = kind;
543 }
544 _ => panic!("Trying to parse a non-variable into a variable"),
545 }
546 debug!("Parsing Variable: {:?}", entity);
547
548 let r#type = entity.get_type().unwrap().get_display_name();
549 trace!("Variable has type: {:?}", r#type);
550
551 Ok(Variable {
552 r#type,
553 kind: variable_kind,
554 })
555 }
556}
557
558impl<'a> TryFrom<clang::Entity<'a>> for Struct {
559 type Error = Error;
560
561 fn try_from(entity: clang::Entity) -> Result<Self, Error> {
562 let struct_kind;
563 match entity.get_kind().try_into() {
564 Ok(ClangEntityKind::Struct(kind)) => {
565 struct_kind = kind;
566 }
567 _ => panic!("Trying to parse a non-class into a class"),
568 }
569 debug!("Parsing Struct: {:?}", entity);
570
571 let mut member_variables = BTreeMap::new();
572 let mut member_structs = BTreeMap::new();
573 let mut member_functions = BTreeMap::new();
574
575 for child in entity.get_children() {
576 trace!("Struct has child: {:?}", child);
577
578 match child.get_kind().try_into() {
579 Ok(ClangEntityKind::Variable(_)) => {
580 let child_usr = child.get_usr().ok_or_else(|| anyhow!("no usr"))?;
581 member_variables.insert(child_usr, Described::<Variable>::try_from(child)?);
582 }
583 Ok(ClangEntityKind::Struct(_)) => {
584 let child_usr: Usr = child.get_usr().ok_or_else(|| anyhow!("no usr"))?;
585 member_structs.insert(child_usr, Described::<Struct>::try_from(child)?);
586 }
587 Ok(ClangEntityKind::Function(_)) => {
588 let child_usr = child.get_usr().ok_or_else(|| anyhow!("no usr"))?;
589 member_functions.insert(child_usr, Described::<Function>::try_from(child)?);
590 }
591 _ => trace!("Skipping child"),
592 }
593 }
594
595 Ok(Struct {
596 kind: struct_kind,
597 member_functions,
598 member_structs,
599 member_variables,
600 })
601 }
602}
603
604impl<'a> TryFrom<clang::Entity<'a>> for Function {
605 type Error = Error;
606
607 fn try_from(entity: clang::Entity) -> Result<Self, Error> {
608 let function_kind;
609 match entity.get_kind().try_into() {
610 Ok(ClangEntityKind::Function(kind)) => {
611 function_kind = kind;
612 }
613 _ => panic!("Trying to parse a non-function into a function"),
614 }
615 debug!("Parsing Function: {:?}", entity);
616
617 let return_type = entity.get_result_type().unwrap().get_display_name();
618 trace!("Function has return type: {:?}", return_type);
619 let arguments = entity
620 .get_arguments()
621 .unwrap()
622 .into_iter()
623 .map(|arg| {
624 let name = arg
625 .get_display_name()
626 .unwrap_or_else(|| String::from("unnamed"));
627 let r#type = arg.get_type().unwrap().get_display_name();
628 trace!("Function has argument {:?} of type {:?}", name, r#type);
629 FunctionArgument { name, r#type }
630 })
631 .collect();
632
633 Ok(Function {
634 kind: function_kind,
635 arguments,
636 return_type,
637 })
638 }
639}
640
641fn get_description(entity: clang::Entity) -> Result<Description> {
642 let name = entity
643 .get_display_name()
644 .ok_or_else(|| anyhow!("Entity has no name: {:?}", entity))?;
645
646 // TODO: is that the best?
647 if let (Some(brief), Some(comment)) = (entity.get_comment_brief(), entity.get_comment()) {
648 Ok(Description {
649 name,
650 brief,
651 detailed: parse_comment(comment),
652 })
653 } else {
654 Ok(Description {
655 name,
656 brief: String::new(),
657 detailed: String::new(),
658 })
659 }
660}
661
662pub fn parse_comment(raw: String) -> String {
663 #[derive(Debug)]
664 enum CommentStyle {
665 // Comments of type `/**` or `/*!`
666 Starred,
667 // Comments of type `///`
668 SingleLine,
669 }
670
671 let mut chars = raw.chars();
672 let style = match &chars.as_str()[..3] {
673 "/*!" | "/**" => CommentStyle::Starred,
674 "///" => CommentStyle::SingleLine,
675 _ => panic!("Comment is empty or doesn't start with either `///`, `/**`, or `/*!`"),
676 };
677
678 chars.nth(2);
679
680 let mut result = String::new();
681
682 'parse_loop: loop {
683 let maybe_space = chars.next();
684 let mut empty_line = false;
685 match maybe_space {
686 // TODO: Warn on empty comments
687 None => break,
688 Some(' ') => {}
689 Some('\n') => {
690 empty_line = true;
691 result.push('\n');
692 }
693 Some(ch) => result.push(ch),
694 }
695
696 if !empty_line {
697 let rest = chars.as_str();
698 match rest.find('\n') {
699 None => {
700 result.push_str(rest);
701 break;
702 }
703 Some(position) => {
704 result.push_str(&rest[..=position]);
705 chars.nth(position);
706 }
707 }
708 }
709
710 // Beginning of the line
711 let first_non_ws_ch = 'ws_loop: loop {
712 let maybe_whitespace = chars.next();
713 match maybe_whitespace {
714 None => break 'parse_loop,
715 Some(ch) if ch.is_whitespace() => continue,
716 Some(ch) => break 'ws_loop ch,
717 }
718 };
719
720 match style {
721 CommentStyle::Starred if first_non_ws_ch == '*' => {
722 if &chars.as_str()[..1] == "/" {
723 break;
724 }
725 }
726 CommentStyle::Starred => result.push(first_non_ws_ch),
727 CommentStyle::SingleLine => {
728 assert!(first_non_ws_ch == '/');
729 let rest = chars.as_str();
730 if &rest[..2] == "//" {
731 chars.nth(1);
732 } else if &rest[..1] == "/" {
733 chars.nth(0);
734 } else {
735 panic!("Could not parse comment");
736 }
737 }
738 }
739 }
740
741 result
742}
743
744#[derive(Debug, Clone, Error)]
745#[error("Failed to load 'compile_commands.json' at path: {:?}", path)]
746pub(crate) struct CompileCommandsLoadError {
747 path: PathBuf,
748}