1use codegraph_parser_api::{
10 ClassEntity, ComplexityBuilder, ComplexityMetrics, Field, FunctionEntity, ImportRelation,
11 Parameter, ParserConfig,
12};
13use tree_sitter::Node;
14
15#[derive(Debug, Clone)]
17pub struct FunctionCall {
18 pub callee: String,
20 pub line: usize,
22 pub caller: Option<String>,
24}
25
26pub struct CVisitor<'a> {
27 pub source: &'a [u8],
28 #[allow(dead_code)]
29 pub config: ParserConfig,
30 pub functions: Vec<FunctionEntity>,
31 pub structs: Vec<ClassEntity>,
32 pub imports: Vec<ImportRelation>,
33 pub calls: Vec<FunctionCall>,
35 extract_calls: bool,
37 current_function: Option<String>,
39}
40
41impl<'a> CVisitor<'a> {
42 pub fn new(source: &'a [u8], config: ParserConfig) -> Self {
43 Self {
44 source,
45 config,
46 functions: Vec::new(),
47 structs: Vec::new(),
48 imports: Vec::new(),
49 calls: Vec::new(),
50 extract_calls: false,
51 current_function: None,
52 }
53 }
54
55 pub fn set_extract_calls(&mut self, extract: bool) {
57 self.extract_calls = extract;
58 }
59
60 fn node_text(&self, node: Node) -> String {
61 node.utf8_text(self.source).unwrap_or("").to_string()
62 }
63
64 pub fn visit_node(&mut self, node: Node) {
65 if node.is_error() {
68 let mut cursor = node.walk();
70 for child in node.children(&mut cursor) {
71 self.visit_node(child);
72 }
73 return;
74 }
75
76 match node.kind() {
77 "function_definition" => self.visit_function(node),
78 "struct_specifier" => self.visit_struct(node),
79 "union_specifier" => self.visit_union(node),
80 "enum_specifier" => self.visit_enum(node),
81 "preproc_include" => self.visit_include(node),
82 "call_expression" if self.extract_calls => self.visit_call(node),
83 _ => {}
84 }
85
86 if node.kind() != "function_definition" {
89 let mut cursor = node.walk();
90 for child in node.children(&mut cursor) {
91 self.visit_node(child);
92 }
93 } else {
94 let mut cursor = node.walk();
97 for child in node.children(&mut cursor) {
98 if child.kind() != "compound_statement" {
99 self.visit_node(child);
100 } else if self.extract_calls {
101 self.visit_node_for_calls(child);
103 }
104 }
105 }
106 }
107
108 fn visit_node_for_calls(&mut self, node: Node) {
110 if node.is_error() {
111 return;
112 }
113
114 if node.kind() == "call_expression" {
115 self.visit_call(node);
116 }
117
118 let mut cursor = node.walk();
119 for child in node.children(&mut cursor) {
120 self.visit_node_for_calls(child);
121 }
122 }
123
124 fn visit_call(&mut self, node: Node) {
126 if let Some(function_node) = node.child_by_field_name("function") {
128 let callee = match function_node.kind() {
129 "identifier" => self.node_text(function_node),
130 "field_expression" => {
131 if let Some(field) = function_node.child_by_field_name("field") {
133 self.node_text(field)
134 } else {
135 self.node_text(function_node)
136 }
137 }
138 "parenthesized_expression" => {
139 "(*indirect)".to_string()
141 }
142 _ => self.node_text(function_node),
143 };
144
145 if !callee.is_empty() {
146 self.calls.push(FunctionCall {
147 callee,
148 line: node.start_position().row + 1,
149 caller: self.current_function.clone(),
150 });
151 }
152 }
153 }
154
155 fn visit_function(&mut self, node: Node) {
156 let mut name = String::new();
157 let mut return_type = String::new();
158 let mut parameters = Vec::new();
159 let mut is_static = false;
160
161 let mut cursor = node.walk();
163 for child in node.children(&mut cursor) {
164 if child.kind() == "storage_class_specifier" {
165 let text = self.node_text(child);
166 if text == "static" {
167 is_static = true;
168 }
169 }
170 }
171
172 if let Some(type_node) = node.child_by_field_name("type") {
174 return_type = self.extract_type_string(type_node);
175 }
176
177 if let Some(declarator) = node.child_by_field_name("declarator") {
179 self.extract_function_declarator(declarator, &mut name, &mut parameters);
180 }
181
182 let prev_function = self.current_function.take();
184 if !name.is_empty() {
185 self.current_function = Some(name.clone());
186 }
187
188 let complexity = node
190 .child_by_field_name("body")
191 .map(|body| self.calculate_complexity(body));
192
193 let visibility = if is_static { "private" } else { "public" };
194
195 let signature = self
196 .node_text(node)
197 .lines()
198 .next()
199 .unwrap_or("")
200 .to_string();
201
202 let func = FunctionEntity {
203 name,
204 signature,
205 visibility: visibility.to_string(),
206 line_start: node.start_position().row + 1,
207 line_end: node.end_position().row + 1,
208 is_async: false,
209 is_test: false,
210 is_static,
211 is_abstract: false,
212 parameters,
213 return_type: if return_type.is_empty() {
214 None
215 } else {
216 Some(return_type)
217 },
218 doc_comment: None,
219 attributes: Vec::new(),
220 parent_class: None,
221 complexity,
222 };
223
224 self.functions.push(func);
225
226 self.current_function = prev_function;
228 }
229
230 fn extract_function_declarator(
231 &self,
232 node: Node,
233 name: &mut String,
234 parameters: &mut Vec<Parameter>,
235 ) {
236 match node.kind() {
237 "function_declarator" => {
238 if let Some(decl) = node.child_by_field_name("declarator") {
240 *name = self.extract_identifier(decl);
241 }
242 if let Some(params) = node.child_by_field_name("parameters") {
244 self.extract_parameters(params, parameters);
245 }
246 }
247 "pointer_declarator" => {
248 let mut cursor = node.walk();
250 for child in node.children(&mut cursor) {
251 if child.kind() == "function_declarator" {
252 self.extract_function_declarator(child, name, parameters);
253 return;
254 }
255 }
256 }
257 "identifier" => {
258 *name = self.node_text(node);
259 }
260 _ => {}
261 }
262 }
263
264 fn extract_identifier(&self, node: Node) -> String {
265 match node.kind() {
266 "identifier" => self.node_text(node),
267 "pointer_declarator" => {
268 let mut cursor = node.walk();
269 for child in node.children(&mut cursor) {
270 let id = self.extract_identifier(child);
271 if !id.is_empty() {
272 return id;
273 }
274 }
275 String::new()
276 }
277 _ => {
278 let mut cursor = node.walk();
279 for child in node.children(&mut cursor) {
280 let id = self.extract_identifier(child);
281 if !id.is_empty() {
282 return id;
283 }
284 }
285 String::new()
286 }
287 }
288 }
289
290 fn extract_parameters(&self, node: Node, parameters: &mut Vec<Parameter>) {
291 let mut cursor = node.walk();
292 for child in node.children(&mut cursor) {
293 if child.kind() == "parameter_declaration" {
294 if let Some(param) = self.extract_parameter(child) {
295 parameters.push(param);
296 }
297 } else if child.kind() == "variadic_parameter" {
298 parameters.push(Parameter {
299 name: "...".to_string(),
300 type_annotation: Some("...".to_string()),
301 default_value: None,
302 is_variadic: true,
303 });
304 }
305 }
306 }
307
308 fn extract_parameter(&self, node: Node) -> Option<Parameter> {
309 let mut type_str = String::new();
310 let mut name = String::new();
311
312 if let Some(type_node) = node.child_by_field_name("type") {
314 type_str = self.extract_type_string(type_node);
315 }
316
317 if let Some(declarator) = node.child_by_field_name("declarator") {
319 let (decl_name, pointer_prefix) = self.extract_declarator_info(declarator);
320 name = decl_name;
321 if !pointer_prefix.is_empty() {
322 type_str = format!("{type_str}{pointer_prefix}");
323 }
324 }
325
326 if name.is_empty() {
328 name = "param".to_string();
329 }
330
331 Some(Parameter {
332 name,
333 type_annotation: if type_str.is_empty() {
334 None
335 } else {
336 Some(type_str)
337 },
338 default_value: None,
339 is_variadic: false,
340 })
341 }
342
343 fn extract_declarator_info(&self, node: Node) -> (String, String) {
344 match node.kind() {
345 "identifier" | "field_identifier" => (self.node_text(node), String::new()),
346 "pointer_declarator" => {
347 let mut pointer_count = 0;
348 let mut cursor = node.walk();
349 for child in node.children(&mut cursor) {
350 if child.kind() == "*" {
351 pointer_count += 1;
352 } else {
353 let (name, extra_ptrs) = self.extract_declarator_info(child);
354 if !name.is_empty() {
355 return (name, "*".repeat(pointer_count) + &extra_ptrs);
356 }
357 }
358 }
359 (String::new(), "*".repeat(pointer_count))
360 }
361 "array_declarator" => {
362 if let Some(decl) = node.child_by_field_name("declarator") {
363 let (name, _) = self.extract_declarator_info(decl);
364 return (name, "[]".to_string());
365 }
366 (String::new(), "[]".to_string())
367 }
368 _ => {
369 let mut cursor = node.walk();
370 for child in node.children(&mut cursor) {
371 let (name, ptrs) = self.extract_declarator_info(child);
372 if !name.is_empty() {
373 return (name, ptrs);
374 }
375 }
376 (String::new(), String::new())
377 }
378 }
379 }
380
381 fn extract_type_string(&self, node: Node) -> String {
382 let text = self.node_text(node);
383 text.trim().to_string()
384 }
385
386 fn visit_struct(&mut self, node: Node) {
387 let has_body = node.child_by_field_name("body").is_some();
389 if !has_body {
390 return;
391 }
392
393 let name = node
394 .child_by_field_name("name")
395 .map(|n| self.node_text(n))
396 .unwrap_or_else(|| format!("__anon_struct_{}", node.start_position().row + 1));
397
398 let fields = self.extract_struct_fields(node);
399
400 let struct_entity = ClassEntity {
401 name,
402 visibility: "public".to_string(),
403 line_start: node.start_position().row + 1,
404 line_end: node.end_position().row + 1,
405 is_abstract: false,
406 is_interface: false,
407 base_classes: Vec::new(),
408 implemented_traits: Vec::new(),
409 methods: Vec::new(),
410 fields,
411 doc_comment: None,
412 attributes: vec!["struct".to_string()],
413 type_parameters: Vec::new(),
414 };
415
416 self.structs.push(struct_entity);
417 }
418
419 fn visit_union(&mut self, node: Node) {
420 let has_body = node.child_by_field_name("body").is_some();
422 if !has_body {
423 return;
424 }
425
426 let name = node
427 .child_by_field_name("name")
428 .map(|n| self.node_text(n))
429 .unwrap_or_else(|| format!("__anon_union_{}", node.start_position().row + 1));
430
431 let fields = self.extract_struct_fields(node);
432
433 let union_entity = ClassEntity {
434 name,
435 visibility: "public".to_string(),
436 line_start: node.start_position().row + 1,
437 line_end: node.end_position().row + 1,
438 is_abstract: false,
439 is_interface: false,
440 base_classes: Vec::new(),
441 implemented_traits: Vec::new(),
442 methods: Vec::new(),
443 fields,
444 doc_comment: None,
445 attributes: vec!["union".to_string()],
446 type_parameters: Vec::new(),
447 };
448
449 self.structs.push(union_entity);
450 }
451
452 fn visit_enum(&mut self, node: Node) {
453 let has_body = node.child_by_field_name("body").is_some();
455 if !has_body {
456 return;
457 }
458
459 let name = node
460 .child_by_field_name("name")
461 .map(|n| self.node_text(n))
462 .unwrap_or_else(|| format!("__anon_enum_{}", node.start_position().row + 1));
463
464 let mut fields = Vec::new();
466 if let Some(body) = node.child_by_field_name("body") {
467 let mut cursor = body.walk();
468 for child in body.children(&mut cursor) {
469 if child.kind() == "enumerator" {
470 let enumerator_name = child
471 .child_by_field_name("name")
472 .map(|n| self.node_text(n))
473 .unwrap_or_default();
474
475 let default_value = child
476 .child_by_field_name("value")
477 .map(|n| self.node_text(n));
478
479 if !enumerator_name.is_empty() {
480 fields.push(Field {
481 name: enumerator_name,
482 type_annotation: Some("int".to_string()),
483 visibility: "public".to_string(),
484 is_static: true,
485 is_constant: true,
486 default_value,
487 });
488 }
489 }
490 }
491 }
492
493 let enum_entity = ClassEntity {
494 name,
495 visibility: "public".to_string(),
496 line_start: node.start_position().row + 1,
497 line_end: node.end_position().row + 1,
498 is_abstract: false,
499 is_interface: false,
500 base_classes: Vec::new(),
501 implemented_traits: Vec::new(),
502 methods: Vec::new(),
503 fields,
504 doc_comment: None,
505 attributes: vec!["enum".to_string()],
506 type_parameters: Vec::new(),
507 };
508
509 self.structs.push(enum_entity);
510 }
511
512 fn extract_struct_fields(&self, node: Node) -> Vec<Field> {
513 let mut fields = Vec::new();
514
515 if let Some(body) = node.child_by_field_name("body") {
516 let mut cursor = body.walk();
517 for child in body.children(&mut cursor) {
518 if child.kind() == "field_declaration" {
519 let field_type = child
520 .child_by_field_name("type")
521 .map(|n| self.extract_type_string(n))
522 .unwrap_or_default();
523
524 let mut field_cursor = child.walk();
527 for field_child in child.children(&mut field_cursor) {
528 match field_child.kind() {
530 "field_identifier" => {
531 let field_name = self.node_text(field_child);
533 if !field_name.is_empty() {
534 fields.push(Field {
535 name: field_name,
536 type_annotation: Some(field_type.clone()),
537 visibility: "public".to_string(),
538 is_static: false,
539 is_constant: false,
540 default_value: None,
541 });
542 }
543 }
544 "pointer_declarator" | "array_declarator" => {
545 let (field_name, pointer_suffix) =
547 self.extract_declarator_info(field_child);
548 let full_type = if pointer_suffix.is_empty() {
549 field_type.clone()
550 } else {
551 format!("{field_type}{pointer_suffix}")
552 };
553
554 if !field_name.is_empty() {
555 fields.push(Field {
556 name: field_name,
557 type_annotation: Some(full_type),
558 visibility: "public".to_string(),
559 is_static: false,
560 is_constant: false,
561 default_value: None,
562 });
563 }
564 }
565 _ => {}
566 }
567 }
568 }
569 }
570 }
571
572 fields
573 }
574
575 fn visit_include(&mut self, node: Node) {
576 let mut path = String::new();
577 let mut is_system = false;
578
579 let mut cursor = node.walk();
580 for child in node.children(&mut cursor) {
581 match child.kind() {
582 "system_lib_string" => {
583 path = self.node_text(child);
585 path = path
586 .trim_start_matches('<')
587 .trim_end_matches('>')
588 .to_string();
589 is_system = true;
590 }
591 "string_literal" => {
592 path = self.node_text(child);
594 path = path.trim_matches('"').to_string();
595 is_system = false;
596 }
597 _ => {}
598 }
599 }
600
601 if !path.is_empty() {
602 let import = ImportRelation {
603 importer: "current_file".to_string(),
604 imported: path,
605 symbols: Vec::new(),
606 is_wildcard: true, alias: if is_system {
608 Some("system".to_string())
609 } else {
610 None
611 },
612 };
613 self.imports.push(import);
614 }
615 }
616
617 fn calculate_complexity(&self, body: Node) -> ComplexityMetrics {
618 let mut builder = ComplexityBuilder::new();
619 self.visit_for_complexity(body, &mut builder);
620 builder.build()
621 }
622
623 fn visit_for_complexity(&self, node: Node, builder: &mut ComplexityBuilder) {
624 match node.kind() {
625 "if_statement" => {
626 builder.add_branch();
627 builder.enter_scope();
628 }
629 "else_clause" => {
630 builder.add_branch();
631 }
632 "for_statement" => {
633 builder.add_loop();
634 builder.enter_scope();
635 }
636 "while_statement" => {
637 builder.add_loop();
638 builder.enter_scope();
639 }
640 "do_statement" => {
641 builder.add_loop();
642 builder.enter_scope();
643 }
644 "switch_statement" => {
645 builder.enter_scope();
646 }
647 "case_statement" => {
648 builder.add_branch();
649 }
650 "default_statement" => {
651 builder.add_branch();
652 }
653 "conditional_expression" => {
654 builder.add_branch();
656 }
657 "goto_statement" => {
658 builder.add_branch();
660 }
661 "binary_expression" => {
662 if let Some(op) = node.child_by_field_name("operator") {
664 let op_text = self.node_text(op);
665 if op_text == "&&" || op_text == "||" {
666 builder.add_logical_operator();
667 }
668 }
669 }
670 "return_statement" => {
671 }
675 _ => {}
676 }
677
678 let mut cursor = node.walk();
679 for child in node.children(&mut cursor) {
680 self.visit_for_complexity(child, builder);
681 }
682
683 match node.kind() {
685 "if_statement" | "for_statement" | "while_statement" | "do_statement"
686 | "switch_statement" => {
687 builder.exit_scope();
688 }
689 _ => {}
690 }
691 }
692}
693
694#[cfg(test)]
695mod tests {
696 use super::*;
697 use tree_sitter::Parser;
698
699 fn parse_and_visit(source: &[u8]) -> CVisitor {
700 let mut parser = Parser::new();
701 parser.set_language(tree_sitter_c::language()).unwrap();
702 let tree = parser.parse(source, None).unwrap();
703
704 let mut visitor = CVisitor::new(source, ParserConfig::default());
705 visitor.visit_node(tree.root_node());
706 visitor
707 }
708
709 #[test]
710 fn test_visitor_basics() {
711 let visitor = CVisitor::new(b"int main() {}", ParserConfig::default());
712 assert_eq!(visitor.functions.len(), 0);
713 assert_eq!(visitor.structs.len(), 0);
714 assert_eq!(visitor.imports.len(), 0);
715 }
716
717 #[test]
718 fn test_visitor_function_extraction() {
719 let source = b"int greet(char *name) { return 0; }";
720 let visitor = parse_and_visit(source);
721
722 assert_eq!(visitor.functions.len(), 1);
723 assert_eq!(visitor.functions[0].name, "greet");
724 assert_eq!(visitor.functions[0].return_type, Some("int".to_string()));
725 }
726
727 #[test]
728 fn test_visitor_static_function() {
729 let source = b"static void helper() {}";
730 let visitor = parse_and_visit(source);
731
732 assert_eq!(visitor.functions.len(), 1);
733 assert_eq!(visitor.functions[0].visibility, "private");
734 assert!(visitor.functions[0].is_static);
735 }
736
737 #[test]
738 fn test_visitor_struct_extraction() {
739 let source = b"struct Person { char *name; int age; };";
740 let visitor = parse_and_visit(source);
741
742 assert_eq!(visitor.structs.len(), 1);
743 assert_eq!(visitor.structs[0].name, "Person");
744 assert_eq!(visitor.structs[0].fields.len(), 2);
745 assert!(visitor.structs[0]
746 .attributes
747 .contains(&"struct".to_string()));
748 }
749
750 #[test]
751 fn test_visitor_union_extraction() {
752 let source = b"union Data { int i; float f; };";
753 let visitor = parse_and_visit(source);
754
755 assert_eq!(visitor.structs.len(), 1);
756 assert_eq!(visitor.structs[0].name, "Data");
757 assert!(visitor.structs[0].attributes.contains(&"union".to_string()));
758 }
759
760 #[test]
761 fn test_visitor_enum_extraction() {
762 let source = b"enum Color { RED, GREEN, BLUE };";
763 let visitor = parse_and_visit(source);
764
765 assert_eq!(visitor.structs.len(), 1);
766 assert_eq!(visitor.structs[0].name, "Color");
767 assert!(visitor.structs[0].attributes.contains(&"enum".to_string()));
768 assert_eq!(visitor.structs[0].fields.len(), 3);
769 }
770
771 #[test]
772 fn test_visitor_system_include() {
773 let source = b"#include <stdio.h>";
774 let visitor = parse_and_visit(source);
775
776 assert_eq!(visitor.imports.len(), 1);
777 assert_eq!(visitor.imports[0].imported, "stdio.h");
778 assert_eq!(visitor.imports[0].alias, Some("system".to_string()));
779 }
780
781 #[test]
782 fn test_visitor_local_include() {
783 let source = b"#include \"myheader.h\"";
784 let visitor = parse_and_visit(source);
785
786 assert_eq!(visitor.imports.len(), 1);
787 assert_eq!(visitor.imports[0].imported, "myheader.h");
788 assert_eq!(visitor.imports[0].alias, None);
789 }
790
791 #[test]
792 fn test_visitor_multiple_includes() {
793 let source = b"#include <stdio.h>\n#include <stdlib.h>\n#include \"myheader.h\"";
794 let visitor = parse_and_visit(source);
795
796 assert_eq!(visitor.imports.len(), 3);
797 }
798
799 #[test]
800 fn test_visitor_function_with_params() {
801 let source = b"int add(int a, int b) { return a + b; }";
802 let visitor = parse_and_visit(source);
803
804 assert_eq!(visitor.functions.len(), 1);
805 assert_eq!(visitor.functions[0].parameters.len(), 2);
806 assert_eq!(visitor.functions[0].parameters[0].name, "a");
807 assert_eq!(visitor.functions[0].parameters[1].name, "b");
808 }
809
810 #[test]
811 fn test_visitor_pointer_params() {
812 let source = b"void process(int *arr, char **argv) {}";
813 let visitor = parse_and_visit(source);
814
815 assert_eq!(visitor.functions.len(), 1);
816 assert_eq!(visitor.functions[0].parameters.len(), 2);
817 let param1 = &visitor.functions[0].parameters[0];
819 assert!(param1
820 .type_annotation
821 .as_ref()
822 .map(|t| t.contains("*"))
823 .unwrap_or(false));
824 }
825
826 #[test]
827 fn test_visitor_variadic_function() {
828 let source = b"int printf(const char *fmt, ...) { return 0; }";
829 let visitor = parse_and_visit(source);
830
831 assert_eq!(visitor.functions.len(), 1);
832 let params = &visitor.functions[0].parameters;
833 assert!(params.iter().any(|p| p.is_variadic));
834 }
835
836 #[test]
837 fn test_visitor_complexity_if() {
838 let source = b"void test() { if (1) {} }";
839 let visitor = parse_and_visit(source);
840
841 assert_eq!(visitor.functions.len(), 1);
842 let complexity = visitor.functions[0].complexity.as_ref().unwrap();
843 assert!(complexity.branches >= 1);
844 }
845
846 #[test]
847 fn test_visitor_complexity_loop() {
848 let source = b"void test() { for (int i = 0; i < 10; i++) {} while(1) {} }";
849 let visitor = parse_and_visit(source);
850
851 assert_eq!(visitor.functions.len(), 1);
852 let complexity = visitor.functions[0].complexity.as_ref().unwrap();
853 assert!(complexity.loops >= 2);
854 }
855
856 #[test]
857 fn test_visitor_complexity_switch() {
858 let source =
859 b"void test(int x) { switch(x) { case 1: break; case 2: break; default: break; } }";
860 let visitor = parse_and_visit(source);
861
862 assert_eq!(visitor.functions.len(), 1);
863 let complexity = visitor.functions[0].complexity.as_ref().unwrap();
864 assert!(complexity.branches >= 3);
865 }
866
867 #[test]
868 fn test_visitor_complexity_logical_operators() {
869 let source = b"void test(int a, int b) { if (a && b || a) {} }";
870 let visitor = parse_and_visit(source);
871
872 assert_eq!(visitor.functions.len(), 1);
873 let complexity = visitor.functions[0].complexity.as_ref().unwrap();
874 assert!(complexity.logical_operators >= 2);
875 }
876
877 #[test]
878 fn test_visitor_complexity_goto() {
879 let source = b"void test() { label: goto label; }";
880 let visitor = parse_and_visit(source);
881
882 assert_eq!(visitor.functions.len(), 1);
883 let complexity = visitor.functions[0].complexity.as_ref().unwrap();
884 assert!(complexity.branches >= 1);
886 }
887
888 #[test]
889 fn test_visitor_forward_declaration_ignored() {
890 let source = b"struct Forward;";
891 let visitor = parse_and_visit(source);
892
893 assert_eq!(visitor.structs.len(), 0);
895 }
896
897 #[test]
898 fn test_visitor_anonymous_struct() {
899 let source = b"struct { int x; int y; } point;";
900 let visitor = parse_and_visit(source);
901
902 assert_eq!(visitor.structs.len(), 1);
903 assert!(visitor.structs[0].name.starts_with("__anon_struct_"));
905 }
906
907 #[test]
908 fn test_visitor_enum_with_values() {
909 let source = b"enum Size { SMALL = 1, MEDIUM = 5, LARGE = 10 };";
910 let visitor = parse_and_visit(source);
911
912 assert_eq!(visitor.structs.len(), 1);
913 let enum_entity = &visitor.structs[0];
914 assert_eq!(enum_entity.fields.len(), 3);
915
916 let small = enum_entity.fields.iter().find(|f| f.name == "SMALL");
918 assert!(small.is_some());
919 assert_eq!(small.unwrap().default_value, Some("1".to_string()));
920 }
921}