1use anyhow::{Context, Result};
7use clang_sys::*;
8use std::ffi::{CStr, CString};
9use std::path::Path;
10use std::process::Command;
11use std::ptr;
12
13fn discover_system_includes() -> Vec<String> {
18 let mut includes = Vec::new();
19
20 let output = Command::new("clang")
22 .args(["-E", "-x", "c", "-", "-v"])
23 .stdin(std::process::Stdio::null())
24 .output();
25
26 if let Ok(output) = output {
27 let stderr = String::from_utf8_lossy(&output.stderr);
28 let mut in_include_section = false;
29
30 for line in stderr.lines() {
31 if line.contains("#include <...> search starts here:") {
32 in_include_section = true;
33 continue;
34 }
35 if line.contains("End of search list.") {
36 break;
37 }
38 if in_include_section {
39 let path = line.trim();
40 if !path.is_empty() && !path.contains("(framework directory)") {
42 includes.push(path.to_string());
43 }
44 }
45 }
46 }
47
48 if includes.is_empty() {
50 if cfg!(target_os = "macos") {
52 includes.extend([
53 "/usr/local/include".to_string(),
54 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include".to_string(),
55 ]);
56 }
57 if cfg!(target_os = "linux") {
59 includes.extend([
60 "/usr/include".to_string(),
61 "/usr/local/include".to_string(),
62 ]);
63 }
64 }
65
66 includes
67}
68
69#[derive(Debug)]
82pub struct CParser {
83 index: CXIndex,
84 system_includes: Vec<String>,
86}
87
88impl CParser {
89 pub fn new() -> Result<Self> {
102 let index = unsafe { clang_createIndex(0, 0) };
104 if index.is_null() {
105 anyhow::bail!("Failed to create clang index");
106 }
107
108 let system_includes = discover_system_includes();
110
111 Ok(Self {
112 index,
113 system_includes,
114 })
115 }
116
117 pub fn parse(&self, source: &str) -> Result<Ast> {
138 let filename = CString::new("input.c").context("Failed to create filename")?;
139 let source_cstr = CString::new(source).context("Failed to convert source to CString")?;
140
141 let mut ast = Ast::new();
142
143 if source.trim().is_empty() {
145 return Ok(ast);
146 }
147
148 let unsaved_file = CXUnsavedFile {
150 Filename: filename.as_ptr(),
151 Contents: source_cstr.as_ptr(),
152 Length: source.len() as std::os::raw::c_ulong,
153 };
154
155 let has_extern_c = source.contains("extern \"C\"");
159 let has_ifdef_guard =
160 source.contains("#ifdef __cplusplus") || source.contains("#if defined(__cplusplus)");
161 let needs_cpp_mode = has_extern_c && !has_ifdef_guard;
162
163 let isystem_flag = CString::new("-isystem").unwrap();
166 let include_cstrings: Vec<CString> = self
167 .system_includes
168 .iter()
169 .map(|p| CString::new(p.as_str()).unwrap())
170 .collect();
171
172 let cpp_flag = CString::new("-x").unwrap();
174 let cpp_lang = CString::new("c++").unwrap();
175
176 let define_eof = CString::new("-DEOF=-1").unwrap();
179 let define_null = CString::new("-DNULL=0").unwrap();
180 let define_bufsiz = CString::new("-DBUFSIZ=8192").unwrap();
182
183 let mut args_vec: Vec<*const std::os::raw::c_char> = Vec::new();
185
186 if needs_cpp_mode {
188 args_vec.push(cpp_flag.as_ptr());
189 args_vec.push(cpp_lang.as_ptr());
190 }
191
192 args_vec.push(define_eof.as_ptr());
194 args_vec.push(define_null.as_ptr());
195 args_vec.push(define_bufsiz.as_ptr());
196
197 for include_path in &include_cstrings {
199 args_vec.push(isystem_flag.as_ptr());
200 args_vec.push(include_path.as_ptr());
201 }
202
203 let flags = 1;
207
208 let mut tu = ptr::null_mut();
209 let result = unsafe {
210 clang_parseTranslationUnit2(
211 self.index,
212 filename.as_ptr(),
213 if args_vec.is_empty() {
214 ptr::null()
215 } else {
216 args_vec.as_ptr()
217 },
218 args_vec.len() as std::os::raw::c_int,
219 &unsaved_file as *const CXUnsavedFile as *mut CXUnsavedFile,
220 1,
221 flags,
222 &mut tu,
223 )
224 };
225
226 if result != CXError_Success || tu.is_null() {
227 anyhow::bail!("Failed to parse C source");
228 }
229
230 let num_diagnostics = unsafe { clang_getNumDiagnostics(tu) };
232 for i in 0..num_diagnostics {
233 let diag = unsafe { clang_getDiagnostic(tu, i) };
234 let severity = unsafe { clang_getDiagnosticSeverity(diag) };
235
236 if severity >= CXDiagnostic_Error {
238 unsafe { clang_disposeDiagnostic(diag) };
239 unsafe { clang_disposeTranslationUnit(tu) };
240 anyhow::bail!("C source has syntax errors");
241 }
242
243 unsafe { clang_disposeDiagnostic(diag) };
244 }
245
246 let cursor = unsafe { clang_getTranslationUnitCursor(tu) };
248
249 let ast_ptr = &mut ast as *mut Ast;
251
252 unsafe {
254 clang_visitChildren(cursor, visit_function, ast_ptr as CXClientData);
255
256 clang_disposeTranslationUnit(tu);
258 }
259
260 Ok(ast)
261 }
262
263 pub fn parse_file(&self, _path: &Path) -> Result<Ast> {
274 Err(anyhow::anyhow!("Not implemented yet"))
276 }
277}
278
279impl Drop for CParser {
280 fn drop(&mut self) {
281 unsafe {
283 clang_disposeIndex(self.index);
284 }
285 }
286}
287
288extern "C" fn visit_function(
294 cursor: CXCursor,
295 _parent: CXCursor,
296 client_data: CXClientData,
297) -> CXChildVisitResult {
298 let ast = unsafe { &mut *(client_data as *mut Ast) };
300
301 let kind = unsafe { clang_getCursorKind(cursor) };
303
304 if kind == 23 {
307 unsafe {
310 clang_visitChildren(cursor, visit_function, client_data);
311 }
312 return CXChildVisit_Continue;
313 }
314
315 if kind == CXCursor_FunctionDecl {
316 if let Some(function) = extract_function(cursor) {
318 ast.add_function(function);
319 }
320 } else if kind == CXCursor_TypedefDecl {
321 let (typedef_opt, struct_opt) = extract_typedef(cursor);
324 if let Some(typedef) = typedef_opt {
325 ast.add_typedef(typedef);
326 }
327 if let Some(struct_def) = struct_opt {
328 ast.add_struct(struct_def);
329 }
330 } else if kind == CXCursor_StructDecl {
331 if let Some(struct_def) = extract_struct(cursor) {
333 ast.add_struct(struct_def);
334 }
335 } else if kind == CXCursor_VarDecl {
336 let semantic_parent = unsafe { clang_getCursorSemanticParent(cursor) };
339 let parent_kind = unsafe { clang_getCursorKind(semantic_parent) };
340
341 let is_file_scope = parent_kind != CXCursor_FunctionDecl;
345
346 if is_file_scope {
347 if let Some(variable) = extract_variable(cursor) {
348 ast.add_variable(variable);
349 }
350 }
351 } else if kind == CXCursor_MacroDefinition {
353 let location = unsafe { clang_getCursorLocation(cursor) };
355 let mut file: CXFile = ptr::null_mut();
356 unsafe {
357 clang_getFileLocation(
358 location,
359 &mut file,
360 ptr::null_mut(),
361 ptr::null_mut(),
362 ptr::null_mut(),
363 );
364 }
365
366 if !file.is_null() {
368 let file_name = unsafe {
369 let name_cxstring = clang_getFileName(file);
370 let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
371 let name = c_str.to_string_lossy().into_owned();
372 clang_disposeString(name_cxstring);
373 name
374 };
375
376 if file_name.ends_with("input.c") {
378 if let Some(macro_def) = extract_macro(cursor) {
379 ast.add_macro(macro_def);
380 }
381 }
382 }
383 }
384
385 CXChildVisit_Recurse
388}
389
390fn extract_function(cursor: CXCursor) -> Option<Function> {
392 let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
394 let name = unsafe {
395 let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
396 let name = c_str.to_string_lossy().into_owned();
397 clang_disposeString(name_cxstring);
398 name
399 };
400
401 let cx_type = unsafe { clang_getCursorType(cursor) };
403 let return_cx_type = unsafe { clang_getResultType(cx_type) };
404 let return_type = convert_type(return_cx_type)?;
405
406 let num_args = unsafe { clang_Cursor_getNumArguments(cursor) };
408 let mut parameters = Vec::new();
409
410 for i in 0..num_args {
411 let arg_cursor = unsafe { clang_Cursor_getArgument(cursor, i as u32) };
413
414 let param_name_cxstring = unsafe { clang_getCursorSpelling(arg_cursor) };
416 let param_name = unsafe {
417 let c_str = CStr::from_ptr(clang_getCString(param_name_cxstring));
418 let name = c_str.to_string_lossy().into_owned();
419 clang_disposeString(param_name_cxstring);
420 name
421 };
422
423 let param_cx_type = unsafe { clang_getCursorType(arg_cursor) };
425 if let Some(param_type) = convert_type(param_cx_type) {
426 let is_pointee_const = unsafe {
428 if param_cx_type.kind == clang_sys::CXType_Pointer {
429 let pointee = clang_sys::clang_getPointeeType(param_cx_type);
430 clang_isConstQualifiedType(pointee) != 0
431 } else {
432 false
433 }
434 };
435 parameters.push(Parameter::new_with_const(
436 param_name,
437 param_type,
438 is_pointee_const,
439 ));
440 }
441 }
442
443 let mut body = Vec::new();
445 let body_ptr = &mut body as *mut Vec<Statement>;
446
447 unsafe {
448 clang_visitChildren(cursor, visit_statement, body_ptr as CXClientData);
449 }
450
451 Some(Function::new_with_body(name, return_type, parameters, body))
452}
453
454fn extract_typedef(cursor: CXCursor) -> (Option<Typedef>, Option<Struct>) {
457 let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
459 let name = unsafe {
460 let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
461 let name = c_str.to_string_lossy().into_owned();
462 clang_disposeString(name_cxstring);
463 name
464 };
465
466 let cx_type = unsafe { clang_getTypedefDeclUnderlyingType(cursor) };
468
469 let canonical = unsafe { clang_getCanonicalType(cx_type) };
472 if canonical.kind == CXType_Record {
473 let decl = unsafe { clang_getTypeDeclaration(canonical) };
474 let struct_name_cxstring = unsafe { clang_getCursorSpelling(decl) };
475 let struct_name = unsafe {
476 let c_str = CStr::from_ptr(clang_getCString(struct_name_cxstring));
477 let sn = c_str.to_string_lossy().into_owned();
478 clang_disposeString(struct_name_cxstring);
479 sn
480 };
481
482 if struct_name.is_empty() {
484 let mut fields = Vec::new();
486 let fields_ptr = &mut fields as *mut Vec<StructField>;
487
488 unsafe {
489 clang_visitChildren(decl, visit_struct_fields, fields_ptr as CXClientData);
490 }
491
492 return (None, Some(Struct::new(name, fields)));
494 }
495 }
496
497 let underlying_type = convert_type(cx_type);
498 match underlying_type {
499 Some(ut) => (Some(Typedef::new(name, ut)), None),
500 None => (None, None),
501 }
502}
503
504fn extract_struct(cursor: CXCursor) -> Option<Struct> {
506 let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
508 let name = unsafe {
509 let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
510 let name = c_str.to_string_lossy().into_owned();
511 clang_disposeString(name_cxstring);
512 name
513 };
514
515 if name.is_empty() {
517 return None;
518 }
519
520 let mut fields = Vec::new();
522 let fields_ptr = &mut fields as *mut Vec<StructField>;
523
524 unsafe {
525 clang_visitChildren(cursor, visit_struct_fields, fields_ptr as CXClientData);
526 }
527
528 Some(Struct::new(name, fields))
529}
530
531fn extract_variable(cursor: CXCursor) -> Option<Variable> {
542 let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
544 let name = unsafe {
545 let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
546 let name = c_str.to_string_lossy().into_owned();
547 clang_disposeString(name_cxstring);
548 name
549 };
550
551 let cx_type = unsafe { clang_getCursorType(cursor) };
553 let var_type = convert_type(cx_type)?;
554
555 let storage_class = unsafe { clang_Cursor_getStorageClass(cursor) };
561 let is_static = storage_class == 3; let is_extern = storage_class == 2; let is_const = unsafe { clang_isConstQualifiedType(cx_type) != 0 };
566
567 let mut initializer: Option<Expression> = None;
569 let initializer_ptr = &mut initializer as *mut Option<Expression>;
570
571 unsafe {
572 clang_visitChildren(
573 cursor,
574 visit_variable_initializer,
575 initializer_ptr as CXClientData,
576 );
577 }
578
579 Some(Variable::new_with_storage_class(
580 name,
581 var_type,
582 initializer,
583 is_static,
584 is_extern,
585 is_const,
586 ))
587}
588
589#[allow(non_upper_case_globals)]
592fn try_extract_expression(cursor: CXCursor) -> Option<Expression> {
593 let kind = unsafe { clang_getCursorKind(cursor) };
594
595 match kind {
596 CXCursor_IntegerLiteral => extract_int_literal(cursor),
597 107 => extract_float_literal(cursor), CXCursor_StringLiteral => extract_string_literal(cursor),
599 110 => extract_char_literal(cursor), CXCursor_DeclRefExpr => extract_variable_ref(cursor),
601 CXCursor_BinaryOperator => extract_binary_op(cursor),
602 CXCursor_CallExpr => extract_function_call(cursor),
603 CXCursor_UnaryOperator => extract_unary_op(cursor),
604 CXCursor_ArraySubscriptExpr => extract_array_index(cursor),
605 CXCursor_MemberRefExpr => extract_field_access(cursor),
606 116 => extract_conditional_op(cursor), 117 => extract_cast(cursor), 118 => extract_compound_literal(cursor), 111 => {
610 let mut result: Option<Expression> = None;
613 let result_ptr = &mut result as *mut Option<Expression>;
614 unsafe {
615 clang_visitChildren(
616 cursor,
617 visit_variable_initializer,
618 result_ptr as CXClientData,
619 );
620 }
621 result
622 }
623 CXCursor_UnexposedExpr => {
624 let mut result: Option<Expression> = None;
626 let result_ptr = &mut result as *mut Option<Expression>;
627 unsafe {
628 clang_visitChildren(
629 cursor,
630 visit_variable_initializer,
631 result_ptr as CXClientData,
632 );
633 }
634 result
635 }
636 _ => None,
637 }
638}
639
640#[allow(non_upper_case_globals)]
642extern "C" fn visit_variable_initializer(
643 cursor: CXCursor,
644 _parent: CXCursor,
645 client_data: CXClientData,
646) -> CXChildVisitResult {
647 let initializer = unsafe { &mut *(client_data as *mut Option<Expression>) };
648
649 if let Some(expr) = try_extract_expression(cursor) {
651 *initializer = Some(expr);
652 return CXChildVisit_Break;
653 }
654
655 CXChildVisit_Continue
656}
657
658fn extract_macro(cursor: CXCursor) -> Option<MacroDefinition> {
665 let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
667 let name = unsafe {
668 let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
669 let name = c_str.to_string_lossy().into_owned();
670 clang_disposeString(name_cxstring);
671 name
672 };
673
674 if name.is_empty() {
676 return None;
677 }
678
679 let is_function_like = unsafe { clang_sys::clang_Cursor_isMacroFunctionLike(cursor) } != 0;
682
683 let range = unsafe { clang_getCursorExtent(cursor) };
685 let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
686
687 let mut tokens: *mut CXToken = ptr::null_mut();
688 let mut num_tokens: u32 = 0;
689
690 unsafe {
691 clang_tokenize(tu, range, &mut tokens, &mut num_tokens);
692 }
693
694 let mut parameters = Vec::new();
697 let mut body_tokens = Vec::new();
698 let mut in_params = false;
699
700 for i in 0..num_tokens {
701 let token = unsafe { *tokens.offset(i as isize) };
702 let token_kind = unsafe { clang_getTokenKind(token) };
703 let token_spelling = unsafe { clang_getTokenSpelling(tu, token) };
704 let token_str = unsafe {
705 let c_str = CStr::from_ptr(clang_getCString(token_spelling));
706 let s = c_str.to_string_lossy().into_owned();
707 clang_disposeString(token_spelling);
708 s
709 };
710
711 if i == 0 {
713 continue;
714 }
715
716 if is_function_like && i == 1 && token_str == "(" {
718 in_params = true;
719 continue;
720 }
721
722 if in_params {
723 if token_str == ")" {
724 in_params = false;
725 continue;
726 } else if token_str != ","
727 && (token_kind == CXToken_Identifier || token_kind == CXToken_Keyword)
728 {
729 parameters.push(token_str);
732 }
733 } else {
734 body_tokens.push(token_str);
735 }
736 }
737
738 unsafe {
740 clang_disposeTokens(tu, tokens, num_tokens);
741 }
742
743 let body = body_tokens.join("");
745
746 if is_function_like {
747 Some(MacroDefinition::new_function_like(name, parameters, body))
748 } else {
749 Some(MacroDefinition::new_object_like(name, body))
750 }
751}
752
753#[allow(non_upper_case_globals)]
759extern "C" fn visit_struct_fields(
760 cursor: CXCursor,
761 _parent: CXCursor,
762 client_data: CXClientData,
763) -> CXChildVisitResult {
764 let fields = unsafe { &mut *(client_data as *mut Vec<StructField>) };
766
767 let kind = unsafe { clang_getCursorKind(cursor) };
769
770 if kind == CXCursor_FieldDecl {
771 let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
773 let name = unsafe {
774 let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
775 let name = c_str.to_string_lossy().into_owned();
776 clang_disposeString(name_cxstring);
777 name
778 };
779
780 let cx_type = unsafe { clang_getCursorType(cursor) };
782 if let Some(field_type) = convert_type(cx_type) {
783 fields.push(StructField::new(name, field_type));
784 }
785 }
786
787 CXChildVisit_Continue
788}
789
790#[allow(non_upper_case_globals)]
796extern "C" fn visit_statement(
797 cursor: CXCursor,
798 _parent: CXCursor,
799 client_data: CXClientData,
800) -> CXChildVisitResult {
801 let statements = unsafe { &mut *(client_data as *mut Vec<Statement>) };
803
804 let kind = unsafe { clang_getCursorKind(cursor) };
806
807 match kind {
808 CXCursor_CompoundStmt => {
809 CXChildVisit_Recurse
811 }
812 CXCursor_DeclStmt => {
813 CXChildVisit_Recurse
815 }
816 CXCursor_VarDecl => {
817 if let Some(stmt) = extract_var_decl(cursor) {
819 statements.push(stmt);
820 }
821 CXChildVisit_Continue
822 }
823 CXCursor_ReturnStmt => {
824 if let Some(stmt) = extract_return_stmt(cursor) {
826 statements.push(stmt);
827 }
828 CXChildVisit_Continue
829 }
830 CXCursor_BinaryOperator => {
831 if let Some(stmt) = extract_assignment_stmt(cursor) {
833 statements.push(stmt);
834 }
835 CXChildVisit_Continue
836 }
837 CXCursor_IfStmt => {
838 if let Some(stmt) = extract_if_stmt(cursor) {
840 statements.push(stmt);
841 }
842 CXChildVisit_Continue
843 }
844 CXCursor_ForStmt => {
845 if let Some(stmt) = extract_for_stmt(cursor) {
847 statements.push(stmt);
848 }
849 CXChildVisit_Continue
850 }
851 CXCursor_WhileStmt => {
852 if let Some(stmt) = extract_while_stmt(cursor) {
854 statements.push(stmt);
855 }
856 CXChildVisit_Continue
857 }
858 CXCursor_SwitchStmt => {
859 if let Some(stmt) = extract_switch_stmt(cursor) {
861 statements.push(stmt);
862 }
863 CXChildVisit_Continue
864 }
865 CXCursor_BreakStmt => {
866 statements.push(Statement::Break);
868 CXChildVisit_Continue
869 }
870 CXCursor_ContinueStmt => {
871 statements.push(Statement::Continue);
873 CXChildVisit_Continue
874 }
875 CXCursor_UnaryOperator => {
876 if let Some(stmt) = extract_inc_dec_stmt(cursor) {
878 statements.push(stmt);
879 }
880 CXChildVisit_Continue
881 }
882 CXCursor_CompoundAssignOperator => {
883 if let Some(stmt) = extract_compound_assignment_stmt(cursor) {
885 statements.push(stmt);
886 }
887 CXChildVisit_Continue
888 }
889 CXCursor_CallExpr => {
890 if let Some(stmt) = extract_statement(cursor) {
893 statements.push(stmt);
894 }
895 CXChildVisit_Continue
896 }
897 _ => CXChildVisit_Recurse, }
899}
900
901fn extract_var_decl(cursor: CXCursor) -> Option<Statement> {
903 let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
905 let name = unsafe {
906 let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
907 let name = c_str.to_string_lossy().into_owned();
908 clang_disposeString(name_cxstring);
909 name
910 };
911
912 let cx_type = unsafe { clang_getCursorType(cursor) };
914 let var_type = convert_type(cx_type)?;
915
916 let mut initializer: Option<Expression> = None;
918 let init_ptr = &mut initializer as *mut Option<Expression>;
919
920 unsafe {
921 clang_visitChildren(cursor, visit_expression, init_ptr as CXClientData);
922 }
923
924 let initializer = match (&var_type, &initializer) {
931 (
932 Type::Array {
933 size: Some(array_size),
934 ..
935 },
936 Some(Expression::IntLiteral(init_val)),
937 ) if i64::from(*init_val) == *array_size => {
938 None
940 }
941 _ => initializer,
942 };
943
944 Some(Statement::VariableDeclaration {
945 name,
946 var_type,
947 initializer,
948 })
949}
950
951fn extract_return_stmt(cursor: CXCursor) -> Option<Statement> {
953 let mut return_expr: Option<Expression> = None;
955 let expr_ptr = &mut return_expr as *mut Option<Expression>;
956
957 unsafe {
958 clang_visitChildren(cursor, visit_expression, expr_ptr as CXClientData);
959 }
960
961 Some(Statement::Return(return_expr))
962}
963
964fn extract_assignment_stmt(cursor: CXCursor) -> Option<Statement> {
966 let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
969 if tu.is_null() {
970 return None;
971 }
972
973 let extent = unsafe { clang_getCursorExtent(cursor) };
975
976 let mut tokens = ptr::null_mut();
978 let mut num_tokens = 0;
979
980 unsafe {
981 clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
982 }
983
984 let mut is_assignment = false;
985
986 for i in 0..num_tokens {
988 unsafe {
989 let token = *tokens.add(i as usize);
990 let token_kind = clang_getTokenKind(token);
991
992 if token_kind == CXToken_Punctuation {
993 let token_cxstring = clang_getTokenSpelling(tu, token);
994 let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
995 if let Ok(token_str) = c_str.to_str() {
996 if token_str == "=" {
998 is_assignment = true;
999 clang_disposeString(token_cxstring);
1000 break;
1001 } else if token_str == "=="
1002 || token_str == "!="
1003 || token_str == "<="
1004 || token_str == ">="
1005 {
1006 clang_disposeString(token_cxstring);
1008 break;
1009 }
1010 }
1011 clang_disposeString(token_cxstring);
1012 }
1013 }
1014 }
1015
1016 unsafe {
1017 clang_disposeTokens(tu, tokens, num_tokens);
1018 }
1019
1020 if !is_assignment {
1021 return None;
1022 }
1023
1024 let mut operands: Vec<Expression> = Vec::new();
1026 let operands_ptr = &mut operands as *mut Vec<Expression>;
1027
1028 unsafe {
1029 clang_visitChildren(cursor, visit_binary_operand, operands_ptr as CXClientData);
1030 }
1031
1032 if operands.len() != 2 {
1034 return None;
1035 }
1036
1037 if let Expression::Dereference(inner) = &operands[0] {
1039 return Some(Statement::DerefAssignment {
1040 target: (**inner).clone(), value: operands[1].clone(),
1042 });
1043 }
1044
1045 if let Expression::ArrayIndex { array, index } = &operands[0] {
1047 return Some(Statement::ArrayIndexAssignment {
1048 array: array.clone(),
1049 index: index.clone(),
1050 value: operands[1].clone(),
1051 });
1052 }
1053
1054 if matches!(
1056 &operands[0],
1057 Expression::PointerFieldAccess { .. } | Expression::FieldAccess { .. }
1058 ) {
1059 let field = match &operands[0] {
1061 Expression::PointerFieldAccess { field, .. } => field.clone(),
1062 Expression::FieldAccess { field, .. } => field.clone(),
1063 _ => unreachable!(),
1064 };
1065
1066 let object = match &operands[0] {
1068 Expression::PointerFieldAccess { pointer, .. } => (**pointer).clone(),
1069 Expression::FieldAccess { object, .. } => (**object).clone(),
1070 _ => unreachable!(),
1071 };
1072
1073 return Some(Statement::FieldAssignment {
1074 object,
1075 field,
1076 value: operands[1].clone(),
1077 });
1078 }
1079
1080 let target = match &operands[0] {
1082 Expression::Variable(name) => name.clone(),
1083 _ => return None, };
1085
1086 Some(Statement::Assignment {
1087 target,
1088 value: operands[1].clone(),
1089 })
1090}
1091
1092fn extract_inc_dec_stmt(cursor: CXCursor) -> Option<Statement> {
1094 let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
1096 if tu.is_null() {
1097 return None;
1098 }
1099
1100 let extent = unsafe { clang_getCursorExtent(cursor) };
1102
1103 let mut tokens = ptr::null_mut();
1105 let mut num_tokens = 0;
1106
1107 unsafe {
1108 clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
1109 }
1110
1111 let mut operator: Option<String> = None;
1112 let mut operator_position = 0;
1113
1114 for i in 0..num_tokens {
1116 unsafe {
1117 let token = *tokens.add(i as usize);
1118 let token_kind = clang_getTokenKind(token);
1119
1120 if token_kind == CXToken_Punctuation {
1121 let token_cxstring = clang_getTokenSpelling(tu, token);
1122 let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
1123 if let Ok(token_str) = c_str.to_str() {
1124 if token_str == "++" || token_str == "--" {
1125 operator = Some(token_str.to_string()); operator_position = i;
1127 clang_disposeString(token_cxstring);
1128 break;
1129 }
1130 }
1131 clang_disposeString(token_cxstring);
1132 }
1133 }
1134 }
1135
1136 let is_pre = operator_position == 0;
1140
1141 unsafe {
1142 clang_disposeTokens(tu, tokens, num_tokens);
1143 }
1144
1145 let mut member_expr: Option<Expression> = None;
1148 let mut simple_var: Option<String> = None;
1149
1150 extern "C" fn visit_for_inc_target(
1152 cursor: CXCursor,
1153 _parent: CXCursor,
1154 client_data: CXClientData,
1155 ) -> CXChildVisitResult {
1156 let data = unsafe { &mut *(client_data as *mut (Option<Expression>, Option<String>)) };
1157 let kind = unsafe { clang_getCursorKind(cursor) };
1158
1159 if kind == CXCursor_MemberRefExpr {
1161 if let Some(expr) = extract_field_access(cursor) {
1162 data.0 = Some(expr);
1163 return CXChildVisit_Break;
1164 }
1165 }
1166
1167 if kind == CXCursor_DeclRefExpr {
1169 let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
1170 let name = unsafe {
1171 let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
1172 let var_name = c_str.to_string_lossy().into_owned();
1173 clang_disposeString(name_cxstring);
1174 var_name
1175 };
1176 data.1 = Some(name);
1177 CXChildVisit_Break
1178 } else {
1179 CXChildVisit_Recurse
1180 }
1181 }
1182
1183 let mut target_data = (member_expr, simple_var);
1184 let target_ptr = &mut target_data as *mut (Option<Expression>, Option<String>);
1185 unsafe {
1186 clang_visitChildren(cursor, visit_for_inc_target, target_ptr as CXClientData);
1187 }
1188 member_expr = target_data.0;
1189 simple_var = target_data.1;
1190
1191 let operator = operator?;
1192 let op_str = operator.as_str();
1193
1194 if let Some(expr) = member_expr {
1197 let delta = match op_str {
1199 "++" => 1,
1200 "--" => -1,
1201 _ => return None,
1202 };
1203
1204 match expr {
1206 Expression::PointerFieldAccess { pointer, field } => {
1207 let value = if delta > 0 {
1209 Expression::BinaryOp {
1210 left: Box::new(Expression::PointerFieldAccess {
1211 pointer: pointer.clone(),
1212 field: field.clone(),
1213 }),
1214 op: BinaryOperator::Add,
1215 right: Box::new(Expression::IntLiteral(1)),
1216 }
1217 } else {
1218 Expression::BinaryOp {
1219 left: Box::new(Expression::PointerFieldAccess {
1220 pointer: pointer.clone(),
1221 field: field.clone(),
1222 }),
1223 op: BinaryOperator::Subtract,
1224 right: Box::new(Expression::IntLiteral(1)),
1225 }
1226 };
1227
1228 return Some(Statement::FieldAssignment {
1229 object: *pointer,
1230 field,
1231 value,
1232 });
1233 }
1234 Expression::FieldAccess { object, field } => {
1235 let value = if delta > 0 {
1237 Expression::BinaryOp {
1238 left: Box::new(Expression::FieldAccess {
1239 object: object.clone(),
1240 field: field.clone(),
1241 }),
1242 op: BinaryOperator::Add,
1243 right: Box::new(Expression::IntLiteral(1)),
1244 }
1245 } else {
1246 Expression::BinaryOp {
1247 left: Box::new(Expression::FieldAccess {
1248 object: object.clone(),
1249 field: field.clone(),
1250 }),
1251 op: BinaryOperator::Subtract,
1252 right: Box::new(Expression::IntLiteral(1)),
1253 }
1254 };
1255
1256 return Some(Statement::FieldAssignment {
1257 object: *object,
1258 field,
1259 value,
1260 });
1261 }
1262 _ => {} }
1264 }
1265
1266 let target = simple_var?;
1268
1269 match op_str {
1270 "++" => {
1271 if is_pre {
1272 Some(Statement::PreIncrement { target })
1273 } else {
1274 Some(Statement::PostIncrement { target })
1275 }
1276 }
1277 "--" => {
1278 if is_pre {
1279 Some(Statement::PreDecrement { target })
1280 } else {
1281 Some(Statement::PostDecrement { target })
1282 }
1283 }
1284 _ => None,
1285 }
1286}
1287
1288fn extract_compound_assignment_stmt(cursor: CXCursor) -> Option<Statement> {
1290 let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
1292 if tu.is_null() {
1293 return None;
1294 }
1295
1296 let extent = unsafe { clang_getCursorExtent(cursor) };
1298
1299 let mut tokens = ptr::null_mut();
1301 let mut num_tokens = 0;
1302
1303 unsafe {
1304 clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
1305 }
1306
1307 let mut operator: Option<BinaryOperator> = None;
1308
1309 for i in 0..num_tokens {
1311 unsafe {
1312 let token = *tokens.add(i as usize);
1313 let token_kind = clang_getTokenKind(token);
1314
1315 if token_kind == CXToken_Punctuation {
1316 let token_cxstring = clang_getTokenSpelling(tu, token);
1317 let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
1318 if let Ok(token_str) = c_str.to_str() {
1319 operator = match token_str {
1320 "+=" => Some(BinaryOperator::Add),
1321 "-=" => Some(BinaryOperator::Subtract),
1322 "*=" => Some(BinaryOperator::Multiply),
1323 "/=" => Some(BinaryOperator::Divide),
1324 "%=" => Some(BinaryOperator::Modulo),
1325 _ => None,
1326 };
1327 if operator.is_some() {
1328 clang_disposeString(token_cxstring);
1329 break;
1330 }
1331 }
1332 clang_disposeString(token_cxstring);
1333 }
1334 }
1335 }
1336
1337 unsafe {
1338 clang_disposeTokens(tu, tokens, num_tokens);
1339 }
1340
1341 let op = operator?;
1342
1343 let mut operands: Vec<Expression> = Vec::new();
1345 let operands_ptr = &mut operands as *mut Vec<Expression>;
1346
1347 unsafe {
1348 clang_visitChildren(cursor, visit_binary_operand, operands_ptr as CXClientData);
1349 }
1350
1351 if operands.len() != 2 {
1353 return None;
1354 }
1355
1356 if let Expression::Dereference(inner) = &operands[0] {
1361 return Some(Statement::DerefCompoundAssignment {
1362 target: (**inner).clone(), op,
1364 value: operands[1].clone(),
1365 });
1366 }
1367
1368 if let Expression::PointerFieldAccess { .. } = &operands[0] {
1370 return Some(Statement::DerefCompoundAssignment {
1371 target: operands[0].clone(), op,
1373 value: operands[1].clone(),
1374 });
1375 }
1376
1377 if let Expression::FieldAccess { .. } = &operands[0] {
1379 return Some(Statement::DerefCompoundAssignment {
1380 target: operands[0].clone(), op,
1382 value: operands[1].clone(),
1383 });
1384 }
1385
1386 if let Expression::ArrayIndex { .. } = &operands[0] {
1388 return Some(Statement::DerefCompoundAssignment {
1389 target: operands[0].clone(), op,
1391 value: operands[1].clone(),
1392 });
1393 }
1394
1395 let target = match &operands[0] {
1397 Expression::Variable(name) => name.clone(),
1398 _ => return None, };
1400
1401 Some(Statement::CompoundAssignment {
1402 target,
1403 op,
1404 value: operands[1].clone(),
1405 })
1406}
1407
1408fn extract_if_stmt(cursor: CXCursor) -> Option<Statement> {
1410 #[repr(C)]
1416 struct IfData {
1417 condition: Option<Expression>,
1418 then_block: Vec<Statement>,
1419 else_block: Option<Vec<Statement>>,
1420 child_index: u32,
1421 }
1422
1423 let mut if_data = IfData {
1424 condition: None,
1425 then_block: Vec::new(),
1426 else_block: None,
1427 child_index: 0,
1428 };
1429
1430 let data_ptr = &mut if_data as *mut IfData;
1431
1432 unsafe {
1433 clang_visitChildren(cursor, visit_if_children, data_ptr as CXClientData);
1434 }
1435
1436 Some(Statement::If {
1437 condition: if_data.condition?,
1438 then_block: if_data.then_block,
1439 else_block: if_data.else_block,
1440 })
1441}
1442
1443#[allow(non_upper_case_globals)]
1445extern "C" fn visit_if_children(
1446 cursor: CXCursor,
1447 _parent: CXCursor,
1448 client_data: CXClientData,
1449) -> CXChildVisitResult {
1450 #[repr(C)]
1451 struct IfData {
1452 condition: Option<Expression>,
1453 then_block: Vec<Statement>,
1454 else_block: Option<Vec<Statement>>,
1455 child_index: u32,
1456 }
1457
1458 let if_data = unsafe { &mut *(client_data as *mut IfData) };
1459 let kind = unsafe { clang_getCursorKind(cursor) };
1460
1461 match if_data.child_index {
1462 0 => {
1463 if_data.condition = match kind {
1466 CXCursor_BinaryOperator => extract_binary_op(cursor),
1467 CXCursor_IntegerLiteral => extract_int_literal(cursor),
1468 107 => extract_float_literal(cursor), 110 => extract_char_literal(cursor), CXCursor_DeclRefExpr => extract_variable_ref(cursor),
1471 CXCursor_CallExpr => extract_function_call(cursor),
1472 CXCursor_UnaryOperator => extract_unary_op(cursor),
1473 _ => {
1474 let mut cond_expr: Option<Expression> = None;
1476 let expr_ptr = &mut cond_expr as *mut Option<Expression>;
1477 unsafe {
1478 clang_visitChildren(cursor, visit_expression, expr_ptr as CXClientData);
1479 }
1480 cond_expr
1481 }
1482 };
1483 if_data.child_index += 1;
1484 CXChildVisit_Continue
1485 }
1486 1 => {
1487 if kind == CXCursor_CompoundStmt {
1490 let body_ptr = &mut if_data.then_block as *mut Vec<Statement>;
1491 unsafe {
1492 clang_visitChildren(cursor, visit_statement, body_ptr as CXClientData);
1493 }
1494 } else {
1495 if let Some(stmt) = extract_single_statement(cursor) {
1497 if_data.then_block.push(stmt);
1498 }
1499 }
1500 if_data.child_index += 1;
1501 CXChildVisit_Continue
1502 }
1503 2 => {
1504 if kind == CXCursor_CompoundStmt {
1507 let mut else_stmts = Vec::new();
1508 let body_ptr = &mut else_stmts as *mut Vec<Statement>;
1509 unsafe {
1510 clang_visitChildren(cursor, visit_statement, body_ptr as CXClientData);
1511 }
1512 if_data.else_block = Some(else_stmts);
1513 } else if kind == CXCursor_IfStmt {
1514 let mut else_stmts = Vec::new();
1516 let body_ptr = &mut else_stmts as *mut Vec<Statement>;
1517 unsafe {
1518 clang_visitChildren(cursor, visit_statement, body_ptr as CXClientData);
1519 }
1520 if_data.else_block = Some(else_stmts);
1521 } else {
1522 if let Some(stmt) = extract_single_statement(cursor) {
1524 if_data.else_block = Some(vec![stmt]);
1525 }
1526 }
1527 if_data.child_index += 1;
1528 CXChildVisit_Continue
1529 }
1530 _ => CXChildVisit_Continue,
1531 }
1532}
1533
1534fn extract_for_stmt(cursor: CXCursor) -> Option<Statement> {
1536 #[repr(C)]
1543 struct ForChildInfo {
1544 cursor: CXCursor,
1545 kind: i32,
1546 }
1547
1548 #[repr(C)]
1549 struct ForCollector {
1550 children: Vec<ForChildInfo>,
1551 }
1552
1553 extern "C" fn collect_for_children(
1555 cursor: CXCursor,
1556 _parent: CXCursor,
1557 client_data: CXClientData,
1558 ) -> CXChildVisitResult {
1559 let collector = unsafe { &mut *(client_data as *mut ForCollector) };
1560 let kind = unsafe { clang_getCursorKind(cursor) };
1561 collector.children.push(ForChildInfo { cursor, kind });
1562 CXChildVisit_Continue
1563 }
1564
1565 let mut collector = ForCollector {
1566 children: Vec::new(),
1567 };
1568
1569 unsafe {
1570 clang_visitChildren(cursor, collect_for_children, &mut collector as *mut _ as CXClientData);
1571 }
1572
1573 let mut init: Option<Box<Statement>> = None;
1575 let mut condition: Option<Expression> = None;
1576 let mut increment: Option<Box<Statement>> = None;
1577 let mut body: Vec<Statement> = Vec::new();
1578
1579 let num_children = collector.children.len();
1580
1581 fn is_assignment_op(cursor: CXCursor) -> bool {
1587 if let Some(op) = extract_binary_operator(cursor) {
1588 matches!(op, BinaryOperator::Assign)
1589 } else {
1590 false
1591 }
1592 }
1593
1594 fn is_condition_op(cursor: CXCursor) -> bool {
1596 if let Some(op) = extract_binary_operator(cursor) {
1597 matches!(
1598 op,
1599 BinaryOperator::Equal |
1600 BinaryOperator::NotEqual |
1601 BinaryOperator::LessThan |
1602 BinaryOperator::GreaterThan |
1603 BinaryOperator::LessEqual |
1604 BinaryOperator::GreaterEqual |
1605 BinaryOperator::LogicalAnd |
1606 BinaryOperator::LogicalOr
1607 )
1608 } else {
1609 false
1610 }
1611 }
1612
1613 if num_children == 0 {
1614 return Some(Statement::For { init, condition, increment, body });
1615 }
1616
1617 let body_idx = num_children - 1;
1620 let body_child = &collector.children[body_idx];
1621
1622 if body_child.kind == CXCursor_CompoundStmt {
1624 let body_ptr = &mut body as *mut Vec<Statement>;
1625 unsafe {
1626 clang_visitChildren(body_child.cursor, visit_statement, body_ptr as CXClientData);
1627 }
1628 } else {
1629 if let Some(stmt) = extract_single_statement(body_child.cursor) {
1631 body.push(stmt);
1632 }
1633 }
1634
1635 let pre_body = &collector.children[..body_idx];
1637
1638 match pre_body.len() {
1639 0 => {
1640 }
1642 1 => {
1643 let child = &pre_body[0];
1646 if child.kind == CXCursor_DeclStmt { let mut init_stmts = Vec::new();
1648 let ptr = &mut init_stmts as *mut Vec<Statement>;
1649 unsafe {
1650 clang_visitChildren(child.cursor, visit_statement, ptr as CXClientData);
1651 }
1652 if let Some(stmt) = init_stmts.into_iter().next() {
1653 init = Some(Box::new(stmt));
1654 }
1655 } else if child.kind == CXCursor_BinaryOperator {
1656 if is_assignment_op(child.cursor) {
1657 if let Some(stmt) = extract_assignment_stmt(child.cursor) {
1659 init = Some(Box::new(stmt));
1660 }
1661 } else if is_condition_op(child.cursor) {
1662 condition = extract_binary_op(child.cursor);
1664 } else {
1665 condition = extract_binary_op(child.cursor);
1667 }
1668 } else if child.kind == CXCursor_UnaryOperator {
1669 if let Some(stmt) = extract_inc_dec_stmt(child.cursor) {
1670 increment = Some(Box::new(stmt));
1671 }
1672 } else {
1673 condition = extract_expression_from_cursor(child.cursor);
1675 }
1676 }
1677 2 => {
1678 let child0 = &pre_body[0];
1681 let child1 = &pre_body[1];
1682
1683 let first_is_init = child0.kind == CXCursor_DeclStmt ||
1685 (child0.kind == CXCursor_BinaryOperator && is_assignment_op(child0.cursor));
1686
1687 if first_is_init {
1688 if child0.kind == CXCursor_DeclStmt {
1690 let mut init_stmts = Vec::new();
1691 let ptr = &mut init_stmts as *mut Vec<Statement>;
1692 unsafe {
1693 clang_visitChildren(child0.cursor, visit_statement, ptr as CXClientData);
1694 }
1695 if let Some(stmt) = init_stmts.into_iter().next() {
1696 init = Some(Box::new(stmt));
1697 }
1698 } else if let Some(stmt) = extract_assignment_stmt(child0.cursor) {
1699 init = Some(Box::new(stmt));
1700 }
1701 condition = extract_expression_from_cursor(child1.cursor);
1702 } else {
1703 condition = extract_expression_from_cursor(child0.cursor);
1705 if child1.kind == CXCursor_BinaryOperator {
1706 if let Some(stmt) = extract_assignment_stmt(child1.cursor) {
1707 increment = Some(Box::new(stmt));
1708 }
1709 } else if child1.kind == CXCursor_UnaryOperator {
1710 if let Some(stmt) = extract_inc_dec_stmt(child1.cursor) {
1711 increment = Some(Box::new(stmt));
1712 }
1713 }
1714 }
1715 }
1716 3 => {
1717 let child0 = &pre_body[0];
1719 let child1 = &pre_body[1];
1720 let child2 = &pre_body[2];
1721
1722 if child0.kind == CXCursor_DeclStmt {
1724 let mut init_stmts = Vec::new();
1725 let ptr = &mut init_stmts as *mut Vec<Statement>;
1726 unsafe {
1727 clang_visitChildren(child0.cursor, visit_statement, ptr as CXClientData);
1728 }
1729 if let Some(stmt) = init_stmts.into_iter().next() {
1730 init = Some(Box::new(stmt));
1731 }
1732 } else if child0.kind == CXCursor_BinaryOperator {
1733 if let Some(stmt) = extract_assignment_stmt(child0.cursor) {
1734 init = Some(Box::new(stmt));
1735 }
1736 }
1737
1738 condition = extract_expression_from_cursor(child1.cursor);
1740
1741 if child2.kind == CXCursor_BinaryOperator {
1743 if let Some(stmt) = extract_assignment_stmt(child2.cursor) {
1744 increment = Some(Box::new(stmt));
1745 }
1746 } else if child2.kind == CXCursor_UnaryOperator {
1747 if let Some(stmt) = extract_inc_dec_stmt(child2.cursor) {
1748 increment = Some(Box::new(stmt));
1749 }
1750 }
1751 }
1752 _ => {
1753 }
1755 }
1756
1757 Some(Statement::For {
1758 init,
1759 condition,
1760 increment,
1761 body,
1762 })
1763}
1764
1765fn extract_expression_from_cursor(cursor: CXCursor) -> Option<Expression> {
1767 let kind = unsafe { clang_getCursorKind(cursor) };
1768 match kind {
1769 CXCursor_BinaryOperator => extract_binary_op(cursor),
1770 CXCursor_IntegerLiteral => extract_int_literal(cursor),
1771 107 => extract_float_literal(cursor), 110 => extract_char_literal(cursor), CXCursor_DeclRefExpr => extract_variable_ref(cursor),
1774 CXCursor_CallExpr => extract_function_call(cursor),
1775 CXCursor_UnaryOperator => extract_unary_op(cursor),
1776 _ => {
1777 let mut expr: Option<Expression> = None;
1778 let expr_ptr = &mut expr as *mut Option<Expression>;
1779 unsafe {
1780 clang_visitChildren(cursor, visit_expression, expr_ptr as CXClientData);
1781 }
1782 expr
1783 }
1784 }
1785}
1786
1787fn extract_single_statement(cursor: CXCursor) -> Option<Statement> {
1789 let kind = unsafe { clang_getCursorKind(cursor) };
1790 match kind {
1791 CXCursor_IfStmt => extract_if_stmt(cursor),
1792 CXCursor_ForStmt => extract_for_stmt(cursor),
1793 CXCursor_WhileStmt => extract_while_stmt(cursor),
1794 CXCursor_ReturnStmt => extract_return_stmt(cursor),
1795 CXCursor_SwitchStmt => extract_switch_stmt(cursor),
1796 CXCursor_UnaryOperator => extract_inc_dec_stmt(cursor),
1797 CXCursor_BinaryOperator => extract_assignment_stmt(cursor),
1798 CXCursor_CallExpr => {
1799 if let Some(Expression::FunctionCall { function, arguments }) = extract_function_call(cursor) {
1800 Some(Statement::FunctionCall { function, arguments })
1801 } else {
1802 None
1803 }
1804 }
1805 CXCursor_BreakStmt => Some(Statement::Break),
1806 CXCursor_ContinueStmt => Some(Statement::Continue),
1807 CXCursor_DoStmt | CXCursor_NullStmt => None, _ => None,
1809 }
1810}
1811fn extract_while_stmt(cursor: CXCursor) -> Option<Statement> {
1813 #[repr(C)]
1818 struct WhileData {
1819 condition: Option<Expression>,
1820 body: Vec<Statement>,
1821 child_index: u32,
1822 }
1823
1824 let mut while_data = WhileData {
1825 condition: None,
1826 body: Vec::new(),
1827 child_index: 0,
1828 };
1829
1830 let data_ptr = &mut while_data as *mut WhileData;
1831
1832 unsafe {
1833 clang_visitChildren(cursor, visit_while_children, data_ptr as CXClientData);
1834 }
1835
1836 Some(Statement::While {
1837 condition: while_data.condition?,
1838 body: while_data.body,
1839 })
1840}
1841
1842#[allow(non_upper_case_globals)]
1844extern "C" fn visit_while_children(
1845 cursor: CXCursor,
1846 _parent: CXCursor,
1847 client_data: CXClientData,
1848) -> CXChildVisitResult {
1849 #[repr(C)]
1850 struct WhileData {
1851 condition: Option<Expression>,
1852 body: Vec<Statement>,
1853 child_index: u32,
1854 }
1855
1856 let while_data = unsafe { &mut *(client_data as *mut WhileData) };
1857 let kind = unsafe { clang_getCursorKind(cursor) };
1858
1859 match while_data.child_index {
1860 0 => {
1861 while_data.condition = match kind {
1864 CXCursor_BinaryOperator => extract_binary_op(cursor),
1865 CXCursor_IntegerLiteral => extract_int_literal(cursor),
1866 107 => extract_float_literal(cursor), 110 => extract_char_literal(cursor), CXCursor_DeclRefExpr => extract_variable_ref(cursor),
1869 CXCursor_CallExpr => extract_function_call(cursor),
1870 CXCursor_UnaryOperator => extract_unary_op(cursor),
1871 _ => {
1872 let mut cond_expr: Option<Expression> = None;
1873 let expr_ptr = &mut cond_expr as *mut Option<Expression>;
1874 unsafe {
1875 clang_visitChildren(cursor, visit_expression, expr_ptr as CXClientData);
1876 }
1877 cond_expr
1878 }
1879 };
1880 while_data.child_index += 1;
1881 CXChildVisit_Continue
1882 }
1883 1 => {
1884 if kind == CXCursor_CompoundStmt {
1886 let body_ptr = &mut while_data.body as *mut Vec<Statement>;
1887 unsafe {
1888 clang_visitChildren(cursor, visit_statement, body_ptr as CXClientData);
1889 }
1890 }
1891 while_data.child_index += 1;
1892 CXChildVisit_Continue
1893 }
1894 _ => CXChildVisit_Continue,
1895 }
1896}
1897
1898#[allow(non_upper_case_globals)]
1902fn extract_switch_stmt(cursor: CXCursor) -> Option<Statement> {
1903 #[repr(C)]
1908 struct SwitchData {
1909 condition: Option<Expression>,
1910 cases: Vec<SwitchCase>,
1911 default_case: Option<Vec<Statement>>,
1912 child_index: u32,
1913 }
1914
1915 let mut switch_data = SwitchData {
1916 condition: None,
1917 cases: Vec::new(),
1918 default_case: None,
1919 child_index: 0,
1920 };
1921
1922 let data_ptr = &mut switch_data as *mut SwitchData;
1923
1924 unsafe {
1925 clang_visitChildren(cursor, visit_switch_children, data_ptr as CXClientData);
1926 }
1927
1928 Some(Statement::Switch {
1929 condition: switch_data.condition?,
1930 cases: switch_data.cases,
1931 default_case: switch_data.default_case,
1932 })
1933}
1934
1935#[allow(non_upper_case_globals)]
1937extern "C" fn visit_switch_children(
1938 cursor: CXCursor,
1939 _parent: CXCursor,
1940 client_data: CXClientData,
1941) -> CXChildVisitResult {
1942 #[repr(C)]
1943 struct SwitchData {
1944 condition: Option<Expression>,
1945 cases: Vec<SwitchCase>,
1946 default_case: Option<Vec<Statement>>,
1947 child_index: u32,
1948 }
1949
1950 let switch_data = unsafe { &mut *(client_data as *mut SwitchData) };
1951 let kind = unsafe { clang_getCursorKind(cursor) };
1952
1953 match switch_data.child_index {
1954 0 => {
1955 if let Some(expr) = try_extract_expression(cursor) {
1957 switch_data.condition = Some(expr);
1958 }
1959 switch_data.child_index += 1;
1960 CXChildVisit_Continue
1961 }
1962 1 => {
1963 if kind == CXCursor_CompoundStmt {
1966 unsafe {
1967 clang_visitChildren(cursor, visit_switch_body, client_data);
1968 }
1969 }
1970 switch_data.child_index += 1;
1971 CXChildVisit_Continue
1972 }
1973 _ => CXChildVisit_Continue,
1974 }
1975}
1976
1977#[allow(non_upper_case_globals)]
1979extern "C" fn visit_switch_body(
1980 cursor: CXCursor,
1981 _parent: CXCursor,
1982 client_data: CXClientData,
1983) -> CXChildVisitResult {
1984 #[repr(C)]
1985 struct SwitchData {
1986 condition: Option<Expression>,
1987 cases: Vec<SwitchCase>,
1988 default_case: Option<Vec<Statement>>,
1989 child_index: u32,
1990 }
1991
1992 let switch_data = unsafe { &mut *(client_data as *mut SwitchData) };
1993 let kind = unsafe { clang_getCursorKind(cursor) };
1994
1995 match kind {
1996 CXCursor_CaseStmt => {
1997 if let Some(case) = extract_case_stmt(cursor) {
1999 switch_data.cases.push(case);
2000 }
2001 CXChildVisit_Continue
2002 }
2003 CXCursor_DefaultStmt => {
2004 if let Some(body) = extract_default_stmt(cursor) {
2006 switch_data.default_case = Some(body);
2007 }
2008 CXChildVisit_Continue
2009 }
2010 _ => CXChildVisit_Continue,
2011 }
2012}
2013
2014fn extract_case_stmt(cursor: CXCursor) -> Option<SwitchCase> {
2016 #[repr(C)]
2021 struct CaseData {
2022 value: Option<Expression>,
2023 body: Vec<Statement>,
2024 child_index: u32,
2025 }
2026
2027 let mut case_data = CaseData {
2028 value: None,
2029 body: Vec::new(),
2030 child_index: 0,
2031 };
2032
2033 let data_ptr = &mut case_data as *mut CaseData;
2034
2035 unsafe {
2036 clang_visitChildren(cursor, visit_case_children, data_ptr as CXClientData);
2037 }
2038
2039 Some(SwitchCase {
2040 value: case_data.value,
2041 body: case_data.body,
2042 })
2043}
2044
2045#[allow(non_upper_case_globals)]
2047extern "C" fn visit_case_children(
2048 cursor: CXCursor,
2049 _parent: CXCursor,
2050 client_data: CXClientData,
2051) -> CXChildVisitResult {
2052 #[repr(C)]
2053 struct CaseData {
2054 value: Option<Expression>,
2055 body: Vec<Statement>,
2056 child_index: u32,
2057 }
2058
2059 let case_data = unsafe { &mut *(client_data as *mut CaseData) };
2060 let _kind = unsafe { clang_getCursorKind(cursor) };
2061
2062 match case_data.child_index {
2063 0 => {
2064 if let Some(expr) = try_extract_expression(cursor) {
2066 case_data.value = Some(expr);
2067 }
2068 case_data.child_index += 1;
2069 CXChildVisit_Continue
2070 }
2071 _ => {
2072 if let Some(stmt) = extract_statement(cursor) {
2075 case_data.body.push(stmt);
2076 }
2077 CXChildVisit_Recurse
2079 }
2080 }
2081}
2082
2083fn extract_default_stmt(cursor: CXCursor) -> Option<Vec<Statement>> {
2085 let mut body: Vec<Statement> = Vec::new();
2087 let body_ptr = &mut body as *mut Vec<Statement>;
2088
2089 unsafe {
2090 clang_visitChildren(cursor, visit_default_children, body_ptr as CXClientData);
2091 }
2092
2093 Some(body)
2094}
2095
2096#[allow(non_upper_case_globals)]
2098extern "C" fn visit_default_children(
2099 cursor: CXCursor,
2100 _parent: CXCursor,
2101 client_data: CXClientData,
2102) -> CXChildVisitResult {
2103 let body = unsafe { &mut *(client_data as *mut Vec<Statement>) };
2104
2105 if let Some(stmt) = extract_statement(cursor) {
2107 body.push(stmt);
2108 }
2109
2110 CXChildVisit_Continue
2111}
2112
2113#[allow(non_upper_case_globals)]
2115fn extract_statement(cursor: CXCursor) -> Option<Statement> {
2116 let kind = unsafe { clang_getCursorKind(cursor) };
2117
2118 match kind {
2119 CXCursor_ReturnStmt => extract_return_stmt(cursor),
2120 CXCursor_VarDecl => extract_var_decl(cursor),
2121 CXCursor_IfStmt => extract_if_stmt(cursor),
2122 CXCursor_ForStmt => extract_for_stmt(cursor),
2123 CXCursor_WhileStmt => extract_while_stmt(cursor),
2124 CXCursor_BreakStmt => Some(Statement::Break),
2125 CXCursor_ContinueStmt => Some(Statement::Continue),
2126 CXCursor_UnaryOperator => extract_inc_dec_stmt(cursor),
2127 CXCursor_BinaryOperator => extract_assignment_stmt(cursor),
2128 CXCursor_CallExpr => {
2129 if let Some(Expression::FunctionCall {
2131 function,
2132 arguments,
2133 }) = extract_function_call(cursor)
2134 {
2135 return Some(Statement::FunctionCall {
2136 function,
2137 arguments,
2138 });
2139 }
2140 None
2141 }
2142 _ => None,
2143 }
2144}
2145
2146#[allow(non_upper_case_globals)]
2152extern "C" fn visit_expression(
2153 cursor: CXCursor,
2154 _parent: CXCursor,
2155 client_data: CXClientData,
2156) -> CXChildVisitResult {
2157 let expr_opt = unsafe { &mut *(client_data as *mut Option<Expression>) };
2159
2160 let kind = unsafe { clang_getCursorKind(cursor) };
2162
2163 match kind {
2164 CXCursor_IntegerLiteral => {
2165 if let Some(expr) = extract_int_literal(cursor) {
2167 *expr_opt = Some(expr);
2168 }
2169 CXChildVisit_Continue
2170 }
2171 107 => {
2172 if let Some(expr) = extract_float_literal(cursor) {
2174 *expr_opt = Some(expr);
2175 }
2176 CXChildVisit_Continue
2177 }
2178 CXCursor_StringLiteral => {
2179 if let Some(expr) = extract_string_literal(cursor) {
2181 *expr_opt = Some(expr);
2182 }
2183 CXChildVisit_Continue
2184 }
2185 110 => {
2186 if let Some(expr) = extract_char_literal(cursor) {
2188 *expr_opt = Some(expr);
2189 }
2190 CXChildVisit_Continue
2191 }
2192 CXCursor_DeclRefExpr => {
2193 if let Some(expr) = extract_variable_ref(cursor) {
2195 *expr_opt = Some(expr);
2196 }
2197 CXChildVisit_Continue
2198 }
2199 CXCursor_BinaryOperator => {
2200 if let Some(expr) = extract_binary_op(cursor) {
2202 *expr_opt = Some(expr);
2203 }
2204 CXChildVisit_Continue
2205 }
2206 CXCursor_CallExpr => {
2207 if let Some(expr) = extract_function_call(cursor) {
2209 *expr_opt = Some(expr);
2210 }
2211 CXChildVisit_Continue
2212 }
2213 CXCursor_UnaryOperator => {
2214 if let Some(expr) = extract_unary_op(cursor) {
2216 *expr_opt = Some(expr);
2217 }
2218 CXChildVisit_Continue
2219 }
2220 CXCursor_ArraySubscriptExpr => {
2221 if let Some(expr) = extract_array_index(cursor) {
2223 *expr_opt = Some(expr);
2224 }
2225 CXChildVisit_Continue
2226 }
2227 CXCursor_MemberRefExpr => {
2228 if let Some(expr) = extract_field_access(cursor) {
2230 *expr_opt = Some(expr);
2231 }
2232 CXChildVisit_Continue
2233 }
2234 116 => {
2235 if let Some(expr) = extract_conditional_op(cursor) {
2238 *expr_opt = Some(expr);
2239 }
2240 CXChildVisit_Continue
2241 }
2242 117 => {
2243 if let Some(expr) = extract_cast(cursor) {
2246 *expr_opt = Some(expr);
2247 }
2248 CXChildVisit_Continue
2249 }
2250 CXCursor_UnexposedExpr => {
2251 CXChildVisit_Recurse
2254 }
2255 CXCursor_ParenExpr => {
2256 CXChildVisit_Recurse
2258 }
2259 136 => {
2260 if let Some(expr) = extract_sizeof(cursor) {
2262 *expr_opt = Some(expr);
2263 CXChildVisit_Continue
2264 } else {
2265 CXChildVisit_Recurse
2267 }
2268 }
2269 119 => {
2270 if let Some(expr) = extract_init_list(cursor) {
2273 *expr_opt = Some(expr);
2274 }
2275 CXChildVisit_Continue
2276 }
2277 _ => CXChildVisit_Recurse,
2278 }
2279}
2280
2281fn extract_int_literal(cursor: CXCursor) -> Option<Expression> {
2283 let extent = unsafe { clang_getCursorExtent(cursor) };
2285
2286 let tu = unsafe {
2288 let loc = clang_getCursorLocation(cursor);
2289 let mut file = ptr::null_mut();
2290 let mut line = 0;
2291 let mut column = 0;
2292 let mut offset = 0;
2293 clang_getFileLocation(loc, &mut file, &mut line, &mut column, &mut offset);
2294
2295 clang_Cursor_getTranslationUnit(cursor)
2298 };
2299
2300 if tu.is_null() {
2301 return Some(Expression::IntLiteral(0));
2302 }
2303
2304 let mut tokens = ptr::null_mut();
2306 let mut num_tokens = 0;
2307
2308 unsafe {
2309 clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
2310 }
2311
2312 let mut value = 0;
2313
2314 if num_tokens > 0 {
2315 unsafe {
2317 let token_cxstring = clang_getTokenSpelling(tu, *tokens);
2318 let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
2319 if let Ok(token_str) = c_str.to_str() {
2320 value = token_str.parse().unwrap_or(0);
2321 }
2322 clang_disposeString(token_cxstring);
2323
2324 clang_disposeTokens(tu, tokens, num_tokens);
2326 }
2327 } else {
2328 unsafe {
2331 let eval_result = clang_Cursor_Evaluate(cursor);
2332 if !eval_result.is_null() {
2333 value = clang_EvalResult_getAsInt(eval_result);
2334 clang_EvalResult_dispose(eval_result);
2335 }
2336 }
2337 }
2338
2339 Some(Expression::IntLiteral(value))
2340}
2341
2342fn extract_float_literal(cursor: CXCursor) -> Option<Expression> {
2344 let extent = unsafe { clang_getCursorExtent(cursor) };
2346
2347 let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
2349
2350 if tu.is_null() {
2351 return Some(Expression::FloatLiteral("0.0".to_string()));
2352 }
2353
2354 let mut tokens = ptr::null_mut();
2356 let mut num_tokens = 0;
2357
2358 unsafe {
2359 clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
2360 }
2361
2362 let mut value = "0.0".to_string();
2363
2364 if num_tokens > 0 {
2365 unsafe {
2367 let token_cxstring = clang_getTokenSpelling(tu, *tokens);
2368 let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
2369 if let Ok(token_str) = c_str.to_str() {
2370 value = token_str.to_string();
2372 }
2373 clang_disposeString(token_cxstring);
2374
2375 clang_disposeTokens(tu, tokens, num_tokens);
2377 }
2378 } else {
2379 unsafe {
2381 let eval_result = clang_Cursor_Evaluate(cursor);
2382 if !eval_result.is_null() {
2383 let float_val = clang_EvalResult_getAsDouble(eval_result);
2384 value = format!("{}", float_val);
2385 clang_EvalResult_dispose(eval_result);
2386 }
2387 }
2388 }
2389
2390 Some(Expression::FloatLiteral(value))
2391}
2392
2393fn extract_string_literal(cursor: CXCursor) -> Option<Expression> {
2395 let extent = unsafe { clang_getCursorExtent(cursor) };
2397
2398 let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
2400
2401 if tu.is_null() {
2402 return Some(Expression::StringLiteral(String::new()));
2403 }
2404
2405 let mut tokens = ptr::null_mut();
2407 let mut num_tokens = 0;
2408
2409 unsafe {
2410 clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
2411 }
2412
2413 let mut value = String::new();
2414
2415 if num_tokens > 0 {
2416 unsafe {
2418 let token_cxstring = clang_getTokenSpelling(tu, *tokens);
2419 let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
2420 if let Ok(token_str) = c_str.to_str() {
2421 value = token_str.trim_matches('"').to_string();
2423 }
2424 clang_disposeString(token_cxstring);
2425
2426 clang_disposeTokens(tu, tokens, num_tokens);
2428 }
2429 }
2430
2431 Some(Expression::StringLiteral(value))
2432}
2433
2434fn extract_char_literal(cursor: CXCursor) -> Option<Expression> {
2437 let extent = unsafe { clang_getCursorExtent(cursor) };
2439
2440 let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
2442
2443 if tu.is_null() {
2444 return Some(Expression::CharLiteral(0));
2445 }
2446
2447 let mut tokens = ptr::null_mut();
2449 let mut num_tokens = 0;
2450
2451 unsafe {
2452 clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
2453 }
2454
2455 let mut value: i8 = 0;
2456
2457 if num_tokens > 0 {
2458 unsafe {
2460 let token_cxstring = clang_getTokenSpelling(tu, *tokens);
2461 let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
2462 if let Ok(token_str) = c_str.to_str() {
2463 let inner = token_str.trim_matches('\'');
2465 value = parse_char_literal(inner);
2466 }
2467 clang_disposeString(token_cxstring);
2468
2469 clang_disposeTokens(tu, tokens, num_tokens);
2471 }
2472 }
2473
2474 Some(Expression::CharLiteral(value))
2475}
2476
2477fn parse_char_literal(s: &str) -> i8 {
2480 if s.is_empty() {
2481 return 0;
2482 }
2483
2484 let mut chars = s.chars();
2485 let first = chars.next().unwrap();
2486
2487 if first == '\\' {
2488 match chars.next() {
2490 Some('0') => 0, Some('n') => b'\n' as i8,
2492 Some('t') => b'\t' as i8,
2493 Some('r') => b'\r' as i8,
2494 Some('\\') => b'\\' as i8,
2495 Some('\'') => b'\'' as i8,
2496 Some('"') => b'"' as i8,
2497 Some('a') => 7, Some('b') => 8, Some('f') => 12, Some('v') => 11, Some('x') => {
2502 let hex: String = chars.take(2).collect();
2504 i8::from_str_radix(&hex, 16).unwrap_or(0)
2505 }
2506 Some(c) if c.is_ascii_digit() => {
2507 let mut octal = String::new();
2509 octal.push(c);
2510 for _ in 0..2 {
2511 if let Some(d) = chars.next() {
2512 if d.is_ascii_digit() && d < '8' {
2513 octal.push(d);
2514 } else {
2515 break;
2516 }
2517 }
2518 }
2519 i8::from_str_radix(&octal, 8).unwrap_or(0)
2520 }
2521 _ => first as i8,
2522 }
2523 } else {
2524 first as i8
2526 }
2527}
2528
2529fn extract_variable_ref(cursor: CXCursor) -> Option<Expression> {
2531 let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
2533 let name = unsafe {
2534 let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
2535 let var_name = c_str.to_string_lossy().into_owned();
2536 clang_disposeString(name_cxstring);
2537 var_name
2538 };
2539
2540 Some(Expression::Variable(name))
2541}
2542
2543fn extract_binary_op(cursor: CXCursor) -> Option<Expression> {
2545 let op = extract_binary_operator(cursor)?;
2547
2548 let mut operands: Vec<Expression> = Vec::new();
2550 let operands_ptr = &mut operands as *mut Vec<Expression>;
2551
2552 unsafe {
2553 clang_visitChildren(cursor, visit_binary_operand, operands_ptr as CXClientData);
2554 }
2555
2556 if operands.len() != 2 {
2558 return None;
2559 }
2560
2561 Some(Expression::BinaryOp {
2562 op,
2563 left: Box::new(operands[0].clone()),
2564 right: Box::new(operands[1].clone()),
2565 })
2566}
2567
2568#[allow(non_upper_case_globals)]
2570extern "C" fn visit_binary_operand(
2571 cursor: CXCursor,
2572 _parent: CXCursor,
2573 client_data: CXClientData,
2574) -> CXChildVisitResult {
2575 let operands = unsafe { &mut *(client_data as *mut Vec<Expression>) };
2576 let kind = unsafe { clang_getCursorKind(cursor) };
2577
2578 match kind {
2579 CXCursor_IntegerLiteral => {
2580 if let Some(expr) = extract_int_literal(cursor) {
2581 operands.push(expr);
2582 }
2583 CXChildVisit_Continue
2584 }
2585 107 => {
2586 if let Some(expr) = extract_float_literal(cursor) {
2588 operands.push(expr);
2589 }
2590 CXChildVisit_Continue
2591 }
2592 CXCursor_StringLiteral => {
2593 if let Some(expr) = extract_string_literal(cursor) {
2594 operands.push(expr);
2595 }
2596 CXChildVisit_Continue
2597 }
2598 110 => {
2599 if let Some(expr) = extract_char_literal(cursor) {
2601 operands.push(expr);
2602 }
2603 CXChildVisit_Continue
2604 }
2605 CXCursor_DeclRefExpr => {
2606 if let Some(expr) = extract_variable_ref(cursor) {
2607 operands.push(expr);
2608 }
2609 CXChildVisit_Continue
2610 }
2611 CXCursor_BinaryOperator => {
2612 if let Some(expr) = extract_binary_op(cursor) {
2614 operands.push(expr);
2615 }
2616 CXChildVisit_Continue
2617 }
2618 CXCursor_UnaryOperator => {
2619 if let Some(expr) = extract_unary_op(cursor) {
2621 operands.push(expr);
2622 }
2623 CXChildVisit_Continue
2624 }
2625 CXCursor_ArraySubscriptExpr => {
2626 if let Some(expr) = extract_array_index(cursor) {
2628 operands.push(expr);
2629 }
2630 CXChildVisit_Continue
2631 }
2632 CXCursor_MemberRefExpr => {
2633 if let Some(expr) = extract_field_access(cursor) {
2635 operands.push(expr);
2636 }
2637 CXChildVisit_Continue
2638 }
2639 CXCursor_UnexposedExpr | CXCursor_ParenExpr => {
2640 if let Some(expr) = extract_sizeof(cursor) {
2642 operands.push(expr);
2643 CXChildVisit_Continue
2644 } else {
2645 CXChildVisit_Recurse
2646 }
2647 }
2648 136 => {
2649 if let Some(expr) = extract_sizeof(cursor) {
2651 operands.push(expr);
2652 CXChildVisit_Continue
2653 } else {
2654 CXChildVisit_Recurse
2655 }
2656 }
2657 CXCursor_CallExpr => {
2658 if let Some(expr) = extract_function_call(cursor) {
2660 operands.push(expr);
2661 }
2662 CXChildVisit_Continue
2663 }
2664 116 => {
2665 if let Some(expr) = extract_conditional_op(cursor) {
2667 operands.push(expr);
2668 }
2669 CXChildVisit_Continue
2670 }
2671 _ => CXChildVisit_Recurse,
2672 }
2673}
2674
2675#[allow(non_upper_case_globals)]
2677fn extract_binary_operator(cursor: CXCursor) -> Option<BinaryOperator> {
2678 let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
2680 if tu.is_null() {
2681 return None;
2682 }
2683
2684 let extent = unsafe { clang_getCursorExtent(cursor) };
2686
2687 let mut tokens = ptr::null_mut();
2689 let mut num_tokens = 0;
2690
2691 unsafe {
2692 clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
2693 }
2694
2695 let mut operator = None;
2696
2697 let mut candidates: Vec<(usize, BinaryOperator)> = Vec::new();
2704 let mut found_first_operand = false;
2705 let mut paren_depth: i32 = 0; for i in 0..num_tokens {
2708 unsafe {
2709 let token = *tokens.add(i as usize);
2710 let token_kind = clang_getTokenKind(token);
2711
2712 if token_kind == CXToken_Identifier || token_kind == CXToken_Literal {
2714 found_first_operand = true;
2715 }
2716
2717 if token_kind == CXToken_Punctuation {
2719 let token_cxstring = clang_getTokenSpelling(tu, token);
2720 let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
2721 if let Ok(token_str) = c_str.to_str() {
2722 match token_str {
2723 "(" => paren_depth += 1,
2724 ")" => paren_depth = paren_depth.saturating_sub(1),
2725 _ => {}
2726 }
2727
2728 if found_first_operand && paren_depth == 0 {
2731 let op = match token_str {
2732 "+" => Some(BinaryOperator::Add),
2733 "-" => Some(BinaryOperator::Subtract),
2734 "*" => Some(BinaryOperator::Multiply),
2735 "/" => Some(BinaryOperator::Divide),
2736 "%" => Some(BinaryOperator::Modulo),
2737 "==" => Some(BinaryOperator::Equal),
2738 "!=" => Some(BinaryOperator::NotEqual),
2739 "<" => Some(BinaryOperator::LessThan),
2740 ">" => Some(BinaryOperator::GreaterThan),
2741 "<=" => Some(BinaryOperator::LessEqual),
2742 ">=" => Some(BinaryOperator::GreaterEqual),
2743 "&&" => Some(BinaryOperator::LogicalAnd),
2744 "||" => Some(BinaryOperator::LogicalOr),
2745 "<<" => Some(BinaryOperator::LeftShift),
2747 ">>" => Some(BinaryOperator::RightShift),
2748 "&" => Some(BinaryOperator::BitwiseAnd),
2749 "|" => Some(BinaryOperator::BitwiseOr),
2750 "^" => Some(BinaryOperator::BitwiseXor),
2751 "=" => Some(BinaryOperator::Assign),
2753 _ => None,
2754 };
2755 if let Some(op) = op {
2756 candidates.push((i as usize, op));
2757 }
2758 }
2759 }
2760 clang_disposeString(token_cxstring);
2761 }
2762 }
2763 }
2764
2765 if !candidates.is_empty() {
2769 for (_, op) in &candidates {
2771 if matches!(op, BinaryOperator::Assign) {
2772 operator = Some(*op);
2773 break;
2774 }
2775 }
2776 if operator.is_none() {
2778 for (_, op) in &candidates {
2779 if matches!(op, BinaryOperator::LogicalOr) {
2780 operator = Some(*op);
2781 break;
2782 }
2783 }
2784 }
2785 if operator.is_none() {
2787 for (_, op) in &candidates {
2788 if matches!(op, BinaryOperator::LogicalAnd) {
2789 operator = Some(*op);
2790 break;
2791 }
2792 }
2793 }
2794 if operator.is_none() {
2796 for (_, op) in &candidates {
2797 if matches!(op, BinaryOperator::BitwiseOr) {
2798 operator = Some(*op);
2799 break;
2800 }
2801 }
2802 }
2803 if operator.is_none() {
2805 for (_, op) in &candidates {
2806 if matches!(op, BinaryOperator::BitwiseXor) {
2807 operator = Some(*op);
2808 break;
2809 }
2810 }
2811 }
2812 if operator.is_none() {
2814 for (_, op) in &candidates {
2815 if matches!(op, BinaryOperator::BitwiseAnd) {
2816 operator = Some(*op);
2817 break;
2818 }
2819 }
2820 }
2821 if operator.is_none() {
2823 for (_, op) in &candidates {
2824 if matches!(op, BinaryOperator::Equal | BinaryOperator::NotEqual) {
2825 operator = Some(*op);
2826 break;
2827 }
2828 }
2829 }
2830 if operator.is_none() {
2832 for (_, op) in &candidates {
2833 if matches!(
2834 op,
2835 BinaryOperator::LessThan
2836 | BinaryOperator::GreaterThan
2837 | BinaryOperator::LessEqual
2838 | BinaryOperator::GreaterEqual
2839 ) {
2840 operator = Some(*op);
2841 break;
2842 }
2843 }
2844 }
2845 if operator.is_none() {
2847 for (_, op) in &candidates {
2848 if matches!(op, BinaryOperator::LeftShift | BinaryOperator::RightShift) {
2849 operator = Some(*op);
2850 break;
2851 }
2852 }
2853 }
2854 if operator.is_none() {
2856 for (_, op) in &candidates {
2857 if matches!(op, BinaryOperator::Add | BinaryOperator::Subtract) {
2858 operator = Some(*op);
2859 break;
2860 }
2861 }
2862 }
2863 if operator.is_none() {
2865 operator = Some(candidates[0].1);
2866 }
2867 }
2868
2869 unsafe {
2870 clang_disposeTokens(tu, tokens, num_tokens);
2871 }
2872
2873 operator
2874}
2875
2876fn extract_function_call(cursor: CXCursor) -> Option<Expression> {
2878 let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
2880 let function = unsafe {
2881 let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
2882 let name = c_str.to_string_lossy().into_owned();
2883 clang_disposeString(name_cxstring);
2884 name
2885 };
2886
2887 #[repr(C)]
2890 struct ArgData {
2891 arguments: Vec<Expression>,
2892 skip_first_declref: bool,
2893 }
2894
2895 let mut arg_data = ArgData {
2896 arguments: Vec::new(),
2897 skip_first_declref: true, };
2899 let args_ptr = &mut arg_data as *mut ArgData;
2900
2901 unsafe {
2902 clang_visitChildren(cursor, visit_call_argument, args_ptr as CXClientData);
2903 }
2904
2905 Some(Expression::FunctionCall {
2906 function,
2907 arguments: arg_data.arguments,
2908 })
2909}
2910
2911#[allow(non_upper_case_globals)]
2917extern "C" fn visit_call_argument(
2918 cursor: CXCursor,
2919 _parent: CXCursor,
2920 client_data: CXClientData,
2921) -> CXChildVisitResult {
2922 #[repr(C)]
2923 struct ArgData {
2924 arguments: Vec<Expression>,
2925 skip_first_declref: bool,
2926 }
2927
2928 let arg_data = unsafe { &mut *(client_data as *mut ArgData) };
2930
2931 let kind = unsafe { clang_getCursorKind(cursor) };
2933
2934 match kind {
2935 CXCursor_IntegerLiteral => {
2936 if let Some(expr) = extract_int_literal(cursor) {
2937 arg_data.arguments.push(expr);
2938 }
2939 CXChildVisit_Continue
2940 }
2941 107 => {
2942 if let Some(expr) = extract_float_literal(cursor) {
2944 arg_data.arguments.push(expr);
2945 }
2946 CXChildVisit_Continue
2947 }
2948 CXCursor_StringLiteral => {
2949 if let Some(expr) = extract_string_literal(cursor) {
2950 arg_data.arguments.push(expr);
2951 }
2952 CXChildVisit_Continue
2953 }
2954 110 => {
2955 if let Some(expr) = extract_char_literal(cursor) {
2957 arg_data.arguments.push(expr);
2958 }
2959 CXChildVisit_Continue
2960 }
2961 CXCursor_DeclRefExpr => {
2962 if arg_data.skip_first_declref {
2965 arg_data.skip_first_declref = false;
2966 CXChildVisit_Continue
2967 } else {
2968 if let Some(expr) = extract_variable_ref(cursor) {
2969 arg_data.arguments.push(expr);
2970 }
2971 CXChildVisit_Continue
2972 }
2973 }
2974 CXCursor_BinaryOperator => {
2975 if let Some(expr) = extract_binary_op(cursor) {
2977 arg_data.arguments.push(expr);
2978 }
2979 CXChildVisit_Continue
2980 }
2981 CXCursor_CallExpr => {
2982 if let Some(expr) = extract_function_call(cursor) {
2984 arg_data.arguments.push(expr);
2985 }
2986 CXChildVisit_Continue
2987 }
2988 CXCursor_UnaryOperator => {
2989 if let Some(expr) = extract_unary_op(cursor) {
2991 arg_data.arguments.push(expr);
2992 }
2993 CXChildVisit_Continue
2994 }
2995 CXCursor_ArraySubscriptExpr => {
2996 if let Some(expr) = extract_array_index(cursor) {
2998 arg_data.arguments.push(expr);
2999 }
3000 CXChildVisit_Continue
3001 }
3002 CXCursor_MemberRefExpr => {
3003 if let Some(expr) = extract_field_access(cursor) {
3005 arg_data.arguments.push(expr);
3006 }
3007 CXChildVisit_Continue
3008 }
3009 116 => {
3010 if let Some(expr) = extract_conditional_op(cursor) {
3012 arg_data.arguments.push(expr);
3013 }
3014 CXChildVisit_Continue
3015 }
3016 CXCursor_UnexposedExpr | CXCursor_ParenExpr => {
3017 if let Some(expr) = extract_sizeof(cursor) {
3019 arg_data.arguments.push(expr);
3020 CXChildVisit_Continue
3021 } else {
3022 CXChildVisit_Recurse
3023 }
3024 }
3025 136 => {
3026 if let Some(expr) = extract_sizeof(cursor) {
3028 arg_data.arguments.push(expr);
3029 CXChildVisit_Continue
3030 } else {
3031 CXChildVisit_Recurse
3032 }
3033 }
3034 _ => CXChildVisit_Continue, }
3036}
3037
3038fn extract_unary_op(cursor: CXCursor) -> Option<Expression> {
3040 let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
3042 if tu.is_null() {
3043 return None;
3044 }
3045
3046 let extent = unsafe { clang_getCursorExtent(cursor) };
3048
3049 let mut tokens = ptr::null_mut();
3051 let mut num_tokens = 0;
3052
3053 unsafe {
3054 clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
3055 }
3056
3057 let mut operator: Option<UnaryOperator> = None;
3058 let mut is_dereference = false;
3059 let mut is_increment = false;
3060 let mut is_decrement = false;
3061 let mut operator_position = 0;
3062
3063 for i in 0..num_tokens {
3065 unsafe {
3066 let token = *tokens.add(i as usize);
3067 let token_kind = clang_getTokenKind(token);
3068
3069 if token_kind == CXToken_Punctuation {
3070 let token_cxstring = clang_getTokenSpelling(tu, token);
3071 let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
3072 if let Ok(token_str) = c_str.to_str() {
3073 match token_str {
3074 "*" => {
3075 is_dereference = true;
3076 clang_disposeString(token_cxstring);
3077 break;
3078 }
3079 "-" => {
3080 operator = Some(UnaryOperator::Minus);
3081 clang_disposeString(token_cxstring);
3082 break;
3083 }
3084 "!" => {
3085 operator = Some(UnaryOperator::LogicalNot);
3086 clang_disposeString(token_cxstring);
3087 break;
3088 }
3089 "~" => {
3090 operator = Some(UnaryOperator::BitwiseNot);
3091 clang_disposeString(token_cxstring);
3092 break;
3093 }
3094 "&" => {
3095 operator = Some(UnaryOperator::AddressOf);
3096 clang_disposeString(token_cxstring);
3097 break;
3098 }
3099 "++" => {
3100 is_increment = true;
3101 operator_position = i;
3102 clang_disposeString(token_cxstring);
3103 break;
3104 }
3105 "--" => {
3106 is_decrement = true;
3107 operator_position = i;
3108 clang_disposeString(token_cxstring);
3109 break;
3110 }
3111 _ => {}
3112 }
3113 }
3114 clang_disposeString(token_cxstring);
3115 }
3116 }
3117 }
3118
3119 unsafe {
3120 clang_disposeTokens(tu, tokens, num_tokens);
3121 }
3122
3123 let mut operand: Option<Expression> = None;
3125 let operand_ptr = &mut operand as *mut Option<Expression>;
3126
3127 unsafe {
3128 clang_visitChildren(cursor, visit_expression, operand_ptr as CXClientData);
3129 }
3130
3131 let operand_expr = operand?;
3132
3133 if is_dereference {
3135 return Some(Expression::Dereference(Box::new(operand_expr)));
3136 }
3137
3138 if is_increment {
3140 let is_pre = operator_position == 0;
3142 if is_pre {
3143 return Some(Expression::PreIncrement {
3144 operand: Box::new(operand_expr),
3145 });
3146 } else {
3147 return Some(Expression::PostIncrement {
3148 operand: Box::new(operand_expr),
3149 });
3150 }
3151 }
3152
3153 if is_decrement {
3154 let is_pre = operator_position == 0;
3156 if is_pre {
3157 return Some(Expression::PreDecrement {
3158 operand: Box::new(operand_expr),
3159 });
3160 } else {
3161 return Some(Expression::PostDecrement {
3162 operand: Box::new(operand_expr),
3163 });
3164 }
3165 }
3166
3167 if let Some(op) = operator {
3169 return Some(Expression::UnaryOp {
3170 op,
3171 operand: Box::new(operand_expr),
3172 });
3173 }
3174
3175 if let Expression::IntLiteral(_) = &operand_expr {
3183 return Some(Expression::UnaryOp {
3185 op: UnaryOperator::Minus,
3186 operand: Box::new(operand_expr),
3187 });
3188 }
3189
3190 None
3191}
3192
3193fn extract_array_index(cursor: CXCursor) -> Option<Expression> {
3195 let mut operands: Vec<Expression> = Vec::new();
3197 let operands_ptr = &mut operands as *mut Vec<Expression>;
3198
3199 unsafe {
3200 clang_visitChildren(cursor, visit_binary_operand, operands_ptr as CXClientData);
3201 }
3202
3203 if operands.len() != 2 {
3205 return None;
3206 }
3207
3208 Some(Expression::ArrayIndex {
3209 array: Box::new(operands[0].clone()),
3210 index: Box::new(operands[1].clone()),
3211 })
3212}
3213
3214fn extract_field_access(cursor: CXCursor) -> Option<Expression> {
3216 let field_name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
3218 let field = unsafe {
3219 let c_str = CStr::from_ptr(clang_getCString(field_name_cxstring));
3220 let name = c_str.to_string_lossy().into_owned();
3221 clang_disposeString(field_name_cxstring);
3222 name
3223 };
3224
3225 let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
3227 if tu.is_null() {
3228 return None;
3229 }
3230
3231 let extent = unsafe { clang_getCursorExtent(cursor) };
3232 let mut tokens = ptr::null_mut();
3233 let mut num_tokens = 0;
3234
3235 unsafe {
3236 clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
3237 }
3238
3239 let mut is_arrow = false;
3240
3241 for i in 0..num_tokens {
3246 unsafe {
3247 let token = *tokens.add(i as usize);
3248 let token_kind = clang_getTokenKind(token);
3249
3250 if token_kind == CXToken_Punctuation {
3251 let token_cxstring = clang_getTokenSpelling(tu, token);
3252 let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
3253 if let Ok(token_str) = c_str.to_str() {
3254 if token_str == "->" {
3255 is_arrow = true;
3256 } else if token_str == "." {
3258 is_arrow = false;
3259 }
3261 }
3262 clang_disposeString(token_cxstring);
3263 }
3264 }
3265 }
3266
3267 unsafe {
3268 clang_disposeTokens(tu, tokens, num_tokens);
3269 }
3270
3271 let mut object_expr: Option<Expression> = None;
3273 let expr_ptr = &mut object_expr as *mut Option<Expression>;
3274
3275 unsafe {
3276 clang_visitChildren(cursor, visit_expression, expr_ptr as CXClientData);
3277 }
3278
3279 let object = object_expr?;
3280
3281 if is_arrow {
3282 Some(Expression::PointerFieldAccess {
3283 pointer: Box::new(object),
3284 field,
3285 })
3286 } else {
3287 Some(Expression::FieldAccess {
3288 object: Box::new(object),
3289 field,
3290 })
3291 }
3292}
3293
3294fn extract_sizeof(cursor: CXCursor) -> Option<Expression> {
3297 let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
3299 if tu.is_null() {
3300 return None;
3301 }
3302
3303 let extent = unsafe { clang_getCursorExtent(cursor) };
3305
3306 let mut tokens = ptr::null_mut();
3308 let mut num_tokens = 0;
3309
3310 unsafe {
3311 clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
3312 }
3313
3314 if num_tokens == 0 {
3317 unsafe {
3318 clang_disposeTokens(tu, tokens, num_tokens);
3319 }
3320 return None;
3321 }
3322
3323 let first_token_is_sizeof = unsafe {
3324 let token = *tokens.add(0);
3325 let token_cxstring = clang_getTokenSpelling(tu, token);
3326 let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
3327 let is_sizeof = c_str.to_str().map(|s| s == "sizeof").unwrap_or(false);
3328 clang_disposeString(token_cxstring);
3329 is_sizeof
3330 };
3331
3332 if !first_token_is_sizeof {
3333 unsafe {
3334 clang_disposeTokens(tu, tokens, num_tokens);
3335 }
3336 return None;
3337 }
3338
3339 let mut type_name = String::new();
3340 let mut paren_depth = 0;
3341 let mut in_sizeof_parens = false;
3342
3343 for i in 1..num_tokens {
3346 unsafe {
3347 let token = *tokens.add(i as usize);
3348 let token_kind = clang_getTokenKind(token);
3349 let token_cxstring = clang_getTokenSpelling(tu, token);
3350 let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
3351
3352 if let Ok(token_str) = c_str.to_str() {
3353 if token_str == "(" {
3354 paren_depth += 1;
3355 in_sizeof_parens = true;
3356 } else if token_str == ")" {
3357 paren_depth -= 1;
3358 if paren_depth == 0 && in_sizeof_parens {
3360 clang_disposeString(token_cxstring);
3361 break;
3362 }
3363 } else if in_sizeof_parens
3364 && (token_kind == CXToken_Identifier || token_kind == CXToken_Keyword)
3365 {
3366 if !type_name.is_empty() {
3368 type_name.push(' ');
3369 }
3370 type_name.push_str(token_str);
3371 }
3372 }
3373
3374 clang_disposeString(token_cxstring);
3375 }
3376 }
3377
3378 unsafe {
3379 clang_disposeTokens(tu, tokens, num_tokens);
3380 }
3381
3382 if !type_name.is_empty() {
3384 Some(Expression::Sizeof { type_name })
3385 } else {
3386 None
3387 }
3388}
3389
3390#[allow(non_upper_case_globals)]
3392fn extract_cast(cursor: CXCursor) -> Option<Expression> {
3397 let target_cx_type = unsafe { clang_getCursorType(cursor) };
3399 let target_type = convert_type(target_cx_type)?;
3400
3401 let mut inner_expr: Option<Expression> = None;
3403 let inner_ptr = &mut inner_expr as *mut Option<Expression>;
3404
3405 unsafe {
3406 clang_visitChildren(cursor, visit_cast_inner, inner_ptr as CXClientData);
3407 }
3408
3409 inner_expr.map(|expr| Expression::Cast {
3410 target_type,
3411 expr: Box::new(expr),
3412 })
3413}
3414
3415#[allow(non_upper_case_globals)]
3417extern "C" fn visit_cast_inner(
3418 cursor: CXCursor,
3419 _parent: CXCursor,
3420 client_data: CXClientData,
3421) -> CXChildVisitResult {
3422 let inner_expr = unsafe { &mut *(client_data as *mut Option<Expression>) };
3423 let kind = unsafe { clang_getCursorKind(cursor) };
3424
3425 if let Some(expr) = try_extract_expression(cursor) {
3427 *inner_expr = Some(expr);
3428 return CXChildVisit_Break; }
3430
3431 match kind {
3433 CXCursor_UnexposedExpr | CXCursor_ParenExpr => CXChildVisit_Recurse,
3434 _ => CXChildVisit_Continue,
3435 }
3436}
3437
3438fn extract_compound_literal(cursor: CXCursor) -> Option<Expression> {
3443 let literal_cx_type = unsafe { clang_getCursorType(cursor) };
3445 let literal_type = convert_type(literal_cx_type)?;
3446
3447 let mut initializers: Vec<Expression> = Vec::new();
3449 let initializers_ptr = &mut initializers as *mut Vec<Expression>;
3450
3451 unsafe {
3452 clang_visitChildren(
3453 cursor,
3454 visit_compound_literal_initializers,
3455 initializers_ptr as CXClientData,
3456 );
3457 }
3458
3459 Some(Expression::CompoundLiteral {
3460 literal_type,
3461 initializers,
3462 })
3463}
3464
3465fn extract_conditional_op(cursor: CXCursor) -> Option<Expression> {
3470 let mut operands: Vec<Expression> = Vec::new();
3472 let operands_ptr = &mut operands as *mut Vec<Expression>;
3473
3474 unsafe {
3475 clang_visitChildren(cursor, visit_conditional_operand, operands_ptr as CXClientData);
3476 }
3477
3478 if operands.len() >= 3 {
3481 Some(Expression::Ternary {
3482 condition: Box::new(operands[0].clone()),
3483 then_expr: Box::new(operands[1].clone()),
3484 else_expr: Box::new(operands[2].clone()),
3485 })
3486 } else if operands.len() == 2 {
3487 Some(Expression::Ternary {
3490 condition: Box::new(operands[0].clone()),
3491 then_expr: Box::new(operands[0].clone()),
3492 else_expr: Box::new(operands[1].clone()),
3493 })
3494 } else {
3495 None
3496 }
3497}
3498
3499#[allow(non_upper_case_globals)]
3502extern "C" fn visit_conditional_operand(
3503 cursor: CXCursor,
3504 _parent: CXCursor,
3505 client_data: CXClientData,
3506) -> CXChildVisitResult {
3507 let operands = unsafe { &mut *(client_data as *mut Vec<Expression>) };
3508
3509 if let Some(expr) = try_extract_expression(cursor) {
3511 operands.push(expr);
3512 }
3513
3514 CXChildVisit_Continue
3515}
3516
3517fn extract_init_list(cursor: CXCursor) -> Option<Expression> {
3523 let literal_cx_type = unsafe { clang_getCursorType(cursor) };
3525 let literal_type = convert_type(literal_cx_type)?;
3526
3527 let mut initializers: Vec<Expression> = Vec::new();
3529 let initializers_ptr = &mut initializers as *mut Vec<Expression>;
3530
3531 unsafe {
3532 clang_visitChildren(
3533 cursor,
3534 visit_init_list_children,
3535 initializers_ptr as CXClientData,
3536 );
3537 }
3538
3539 Some(Expression::CompoundLiteral {
3540 literal_type,
3541 initializers,
3542 })
3543}
3544
3545#[allow(non_upper_case_globals)]
3548extern "C" fn visit_init_list_children(
3549 cursor: CXCursor,
3550 _parent: CXCursor,
3551 client_data: CXClientData,
3552) -> CXChildVisitResult {
3553 let initializers = unsafe { &mut *(client_data as *mut Vec<Expression>) };
3554 let kind = unsafe { clang_getCursorKind(cursor) };
3555
3556 if kind == 115 {
3559 let cx_type = unsafe { clang_getCursorType(cursor) };
3561 if let Some(var_type) = convert_type(cx_type) {
3562 let default_expr = match var_type {
3564 Type::Int => Expression::IntLiteral(0),
3565 Type::Float | Type::Double => Expression::IntLiteral(0), Type::Char => Expression::IntLiteral(0),
3567 _ => Expression::IntLiteral(0), };
3569 initializers.push(default_expr);
3570 }
3571 return CXChildVisit_Continue;
3572 }
3573
3574 if kind == CXCursor_UnexposedExpr {
3578 #[repr(C)]
3580 struct ChildInfo {
3581 kinds: Vec<u32>,
3582 exprs: Vec<Expression>,
3583 }
3584
3585 let mut info = ChildInfo {
3586 kinds: Vec::new(),
3587 exprs: Vec::new(),
3588 };
3589 let info_ptr = &mut info as *mut ChildInfo;
3590
3591 extern "C" fn collect_child_info(
3592 cursor: CXCursor,
3593 _parent: CXCursor,
3594 client_data: CXClientData,
3595 ) -> CXChildVisitResult {
3596 let info = unsafe { &mut *(client_data as *mut ChildInfo) };
3597 let kind = unsafe { clang_getCursorKind(cursor) };
3598 info.kinds.push(kind as u32);
3599
3600 if kind == 119 {
3602 if let Some(expr) = extract_init_list(cursor) {
3604 info.exprs.push(expr);
3605 }
3606 } else if let Some(expr) = try_extract_expression(cursor) {
3607 info.exprs.push(expr);
3608 }
3609 CXChildVisit_Continue
3610 }
3611
3612 unsafe {
3613 clang_visitChildren(cursor, collect_child_info, info_ptr as CXClientData);
3614 }
3615
3616 if info.exprs.len() == 2 && matches!(&info.exprs[0], Expression::IntLiteral(_)) {
3618 initializers.push(info.exprs[1].clone());
3619 return CXChildVisit_Continue;
3620 }
3621
3622 if info.kinds.len() == 2 && info.kinds[0] == 47 && !info.exprs.is_empty() {
3625 initializers.push(info.exprs.last().unwrap().clone());
3627 return CXChildVisit_Continue;
3628 }
3629
3630 return CXChildVisit_Recurse;
3632 }
3633
3634 if let Some(expr) = try_extract_expression(cursor) {
3636 initializers.push(expr);
3637 return CXChildVisit_Continue;
3638 }
3639
3640 match kind {
3642 CXCursor_ParenExpr => CXChildVisit_Recurse,
3643 _ => CXChildVisit_Continue,
3644 }
3645}
3646
3647#[allow(non_upper_case_globals)]
3649extern "C" fn visit_compound_literal_initializers(
3650 cursor: CXCursor,
3651 _parent: CXCursor,
3652 client_data: CXClientData,
3653) -> CXChildVisitResult {
3654 let initializers = unsafe { &mut *(client_data as *mut Vec<Expression>) };
3655 let kind = unsafe { clang_getCursorKind(cursor) };
3656
3657 if kind == 119 {
3660 return CXChildVisit_Recurse;
3662 }
3663
3664 if let Some(expr) = try_extract_expression(cursor) {
3666 initializers.push(expr);
3667 return CXChildVisit_Continue;
3668 }
3669
3670 match kind {
3672 CXCursor_UnexposedExpr | CXCursor_ParenExpr => CXChildVisit_Recurse,
3673 _ => CXChildVisit_Continue,
3674 }
3675}
3676
3677#[allow(non_upper_case_globals)]
3678fn convert_type(cx_type: CXType) -> Option<Type> {
3679 match cx_type.kind {
3681 CXType_Void => Some(Type::Void),
3682 CXType_Int => Some(Type::Int),
3683 CXType_UInt => Some(Type::UnsignedInt), CXType_UChar => Some(Type::Char), CXType_UShort => Some(Type::UnsignedInt), CXType_ULong => Some(Type::UnsignedInt), CXType_Short => Some(Type::Int), CXType_Long => Some(Type::Int), CXType_LongLong => Some(Type::Int), CXType_ULongLong => Some(Type::UnsignedInt), CXType_Float => Some(Type::Float),
3692 CXType_Double => Some(Type::Double),
3693 CXType_Char_S | CXType_Char_U => Some(Type::Char),
3694 CXType_Pointer => {
3695 let pointee = unsafe { clang_getPointeeType(cx_type) };
3697
3698 if pointee.kind == CXType_FunctionProto || pointee.kind == CXType_FunctionNoProto {
3700 let return_cx_type = unsafe { clang_getResultType(pointee) };
3703 let return_type = convert_type(return_cx_type)?;
3704
3705 let num_args = unsafe { clang_getNumArgTypes(pointee) };
3707 let mut param_types = Vec::new();
3708
3709 for i in 0..num_args {
3710 let arg_type = unsafe { clang_getArgType(pointee, i as u32) };
3711 if let Some(param_type) = convert_type(arg_type) {
3712 param_types.push(param_type);
3713 }
3714 }
3715
3716 return Some(Type::FunctionPointer {
3717 param_types,
3718 return_type: Box::new(return_type),
3719 });
3720 }
3721
3722 convert_type(pointee).map(|t| Type::Pointer(Box::new(t)))
3724 }
3725 CXType_FunctionProto | CXType_FunctionNoProto => {
3726 let return_cx_type = unsafe { clang_getResultType(cx_type) };
3730 let return_type = convert_type(return_cx_type)?;
3731
3732 let num_args = unsafe { clang_getNumArgTypes(cx_type) };
3734 let mut param_types = Vec::new();
3735
3736 for i in 0..num_args {
3737 let arg_type = unsafe { clang_getArgType(cx_type, i as u32) };
3738 if let Some(param_type) = convert_type(arg_type) {
3739 param_types.push(param_type);
3740 }
3741 }
3742
3743 Some(Type::FunctionPointer {
3744 param_types,
3745 return_type: Box::new(return_type),
3746 })
3747 }
3748 CXType_Record => {
3749 let decl = unsafe { clang_getTypeDeclaration(cx_type) };
3751 let name_cxstring = unsafe { clang_getCursorSpelling(decl) };
3752 let name = unsafe {
3753 let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
3754 let struct_name = c_str.to_string_lossy().into_owned();
3755 clang_disposeString(name_cxstring);
3756 struct_name
3757 };
3758 Some(Type::Struct(name))
3759 }
3760 CXType_Elaborated => {
3761 let canonical = unsafe { clang_getCanonicalType(cx_type) };
3764 convert_type(canonical)
3765 }
3766 CXType_Typedef => {
3767 let typedef_decl = unsafe { clang_getTypeDeclaration(cx_type) };
3769 let typedef_name_cxstring = unsafe { clang_getCursorSpelling(typedef_decl) };
3770 let typedef_name = unsafe {
3771 let c_str = CStr::from_ptr(clang_getCString(typedef_name_cxstring));
3772 let tn = c_str.to_string_lossy().into_owned();
3773 clang_disposeString(typedef_name_cxstring);
3774 tn
3775 };
3776
3777 match typedef_name.as_str() {
3780 "size_t" | "ssize_t" | "ptrdiff_t" => {
3781 return Some(Type::TypeAlias(typedef_name));
3782 }
3783 _ => {}
3784 }
3785
3786 let canonical = unsafe { clang_getCanonicalType(cx_type) };
3789
3790 if canonical.kind == CXType_Record {
3792 let decl = unsafe { clang_getTypeDeclaration(canonical) };
3793 let struct_name_cxstring = unsafe { clang_getCursorSpelling(decl) };
3794 let struct_name = unsafe {
3795 let c_str = CStr::from_ptr(clang_getCString(struct_name_cxstring));
3796 let sn = c_str.to_string_lossy().into_owned();
3797 clang_disposeString(struct_name_cxstring);
3798 sn
3799 };
3800
3801 if struct_name.is_empty() {
3803 return Some(Type::Struct(typedef_name));
3804 }
3805 }
3806
3807 convert_type(canonical)
3809 }
3810 CXType_ConstantArray => {
3811 let element_cx_type = unsafe { clang_getArrayElementType(cx_type) };
3813 let element_type = convert_type(element_cx_type)?;
3814
3815 let array_size = unsafe { clang_getArraySize(cx_type) };
3817 let size = if array_size >= 0 {
3818 Some(array_size)
3819 } else {
3820 None
3821 };
3822
3823 Some(Type::Array {
3824 element_type: Box::new(element_type),
3825 size,
3826 })
3827 }
3828 114 => {
3829 let element_cx_type = unsafe { clang_getArrayElementType(cx_type) };
3833 let element_type = convert_type(element_cx_type)?;
3834
3835 Some(Type::Array {
3837 element_type: Box::new(element_type),
3838 size: None,
3839 })
3840 }
3841 _ => None,
3842 }
3843}
3844
3845#[derive(Debug, Clone, PartialEq)]
3847pub struct SwitchCase {
3848 pub value: Option<Expression>,
3850 pub body: Vec<Statement>,
3852}
3853
3854#[derive(Debug, Clone, PartialEq)]
3856pub enum Statement {
3857 VariableDeclaration {
3859 name: String,
3861 var_type: Type,
3863 initializer: Option<Expression>,
3865 },
3866 Return(Option<Expression>),
3868 Assignment {
3870 target: String,
3872 value: Expression,
3874 },
3875 If {
3877 condition: Expression,
3879 then_block: Vec<Statement>,
3881 else_block: Option<Vec<Statement>>,
3883 },
3884 For {
3886 init: Option<Box<Statement>>,
3888 condition: Option<Expression>,
3890 increment: Option<Box<Statement>>,
3892 body: Vec<Statement>,
3894 },
3895 While {
3897 condition: Expression,
3899 body: Vec<Statement>,
3901 },
3902 DerefAssignment {
3904 target: Expression,
3906 value: Expression,
3908 },
3909 ArrayIndexAssignment {
3911 array: Box<Expression>,
3913 index: Box<Expression>,
3915 value: Expression,
3917 },
3918 FieldAssignment {
3920 object: Expression,
3922 field: String,
3924 value: Expression,
3926 },
3927 Break,
3929 Continue,
3931 Switch {
3933 condition: Expression,
3935 cases: Vec<SwitchCase>,
3937 default_case: Option<Vec<Statement>>,
3939 },
3940 PostIncrement {
3942 target: String,
3944 },
3945 PreIncrement {
3947 target: String,
3949 },
3950 PostDecrement {
3952 target: String,
3954 },
3955 PreDecrement {
3957 target: String,
3959 },
3960 CompoundAssignment {
3962 target: String,
3964 op: BinaryOperator,
3966 value: Expression,
3968 },
3969 DerefCompoundAssignment {
3972 target: Expression,
3974 op: BinaryOperator,
3976 value: Expression,
3978 },
3979 FunctionCall {
3981 function: String,
3983 arguments: Vec<Expression>,
3985 },
3986}
3987
3988impl Statement {
3989 pub fn is_string_function_call(&self) -> bool {
3991 match self {
3992 Statement::FunctionCall { function, .. } => {
3993 matches!(function.as_str(), "strlen" | "strcmp" | "strcpy" | "strdup")
3994 }
3995 _ => false,
3996 }
3997 }
3998
3999 pub fn is_function_call(&self) -> bool {
4001 matches!(self, Statement::FunctionCall { .. })
4002 }
4003
4004 pub fn as_function_call(&self) -> Option<&Expression> {
4013 None
4014 }
4015}
4016
4017#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4019pub enum UnaryOperator {
4020 Minus,
4022 LogicalNot,
4024 BitwiseNot,
4026 AddressOf,
4028}
4029
4030#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4032pub enum BinaryOperator {
4033 Add,
4035 Subtract,
4037 Multiply,
4039 Divide,
4041 Modulo,
4043 Equal,
4045 NotEqual,
4047 LessThan,
4049 GreaterThan,
4051 LessEqual,
4053 GreaterEqual,
4055 LogicalAnd,
4057 LogicalOr,
4059 LeftShift,
4061 RightShift,
4063 BitwiseAnd,
4065 BitwiseOr,
4067 BitwiseXor,
4069 Assign,
4071}
4072
4073#[derive(Debug, Clone, PartialEq)]
4075pub enum Expression {
4076 IntLiteral(i32),
4078 FloatLiteral(String),
4080 StringLiteral(String),
4082 CharLiteral(i8),
4084 Variable(String),
4086 BinaryOp {
4088 op: BinaryOperator,
4090 left: Box<Expression>,
4092 right: Box<Expression>,
4094 },
4095 FunctionCall {
4097 function: String,
4099 arguments: Vec<Expression>,
4101 },
4102 Dereference(Box<Expression>),
4104 UnaryOp {
4106 op: UnaryOperator,
4108 operand: Box<Expression>,
4110 },
4111 ArrayIndex {
4113 array: Box<Expression>,
4115 index: Box<Expression>,
4117 },
4118 FieldAccess {
4120 object: Box<Expression>,
4122 field: String,
4124 },
4125 PointerFieldAccess {
4127 pointer: Box<Expression>,
4129 field: String,
4131 },
4132 PostIncrement {
4134 operand: Box<Expression>,
4136 },
4137 PreIncrement {
4139 operand: Box<Expression>,
4141 },
4142 PostDecrement {
4144 operand: Box<Expression>,
4146 },
4147 PreDecrement {
4149 operand: Box<Expression>,
4151 },
4152 Sizeof {
4154 type_name: String,
4156 },
4157 Cast {
4170 target_type: Type,
4172 expr: Box<Expression>,
4174 },
4175 CompoundLiteral {
4188 literal_type: Type,
4190 initializers: Vec<Expression>,
4192 },
4193 Ternary {
4209 condition: Box<Expression>,
4211 then_expr: Box<Expression>,
4213 else_expr: Box<Expression>,
4215 },
4216}
4217
4218impl Expression {
4219 pub fn is_string_function_call(&self) -> bool {
4221 match self {
4222 Expression::FunctionCall { function, .. } => {
4223 matches!(function.as_str(), "strlen" | "strcmp" | "strcpy" | "strdup")
4224 }
4225 _ => false,
4226 }
4227 }
4228
4229 pub fn string_function_name(&self) -> Option<&str> {
4231 match self {
4232 Expression::FunctionCall { function, .. } if self.is_string_function_call() => {
4233 Some(function.as_str())
4234 }
4235 _ => None,
4236 }
4237 }
4238
4239 pub fn has_string_literal_argument(&self) -> bool {
4241 match self {
4242 Expression::FunctionCall { arguments, .. } => arguments
4243 .iter()
4244 .any(|arg| matches!(arg, Expression::StringLiteral(_))),
4245 _ => false,
4246 }
4247 }
4248}
4249
4250#[derive(Debug, Clone, PartialEq)]
4252pub struct Typedef {
4253 pub name: String,
4255 pub underlying_type: Type,
4257}
4258
4259impl Typedef {
4260 pub fn new(name: String, underlying_type: Type) -> Self {
4262 Self {
4263 name,
4264 underlying_type,
4265 }
4266 }
4267
4268 pub fn name(&self) -> &str {
4270 &self.name
4271 }
4272
4273 pub fn underlying_type(&self) -> &str {
4275 match &self.underlying_type {
4277 Type::Void => "void",
4278 Type::Int => "int",
4279 Type::UnsignedInt => "unsigned int", Type::Float => "float",
4281 Type::Double => "double",
4282 Type::Char => "char",
4283 Type::Pointer(inner) => match **inner {
4284 Type::Char => "char*",
4285 Type::Int => "int*",
4286 Type::UnsignedInt => "unsigned int*", Type::Float => "float*",
4288 Type::Double => "double*",
4289 Type::Void => "void*",
4290 _ => "pointer",
4291 },
4292 Type::Struct(name) => name,
4293 Type::FunctionPointer { .. } => "function pointer",
4294 Type::Array { .. } => "array",
4295 Type::TypeAlias(name) => name,
4297 }
4298 }
4299
4300 pub fn is_pointer(&self) -> bool {
4302 matches!(self.underlying_type, Type::Pointer(_))
4303 }
4304
4305 pub fn is_struct(&self) -> bool {
4307 matches!(self.underlying_type, Type::Struct(_))
4308 }
4309
4310 pub fn is_function_pointer(&self) -> bool {
4312 matches!(self.underlying_type, Type::FunctionPointer { .. })
4313 }
4314
4315 pub fn is_array(&self) -> bool {
4317 false
4319 }
4320}
4321
4322#[derive(Debug, Clone, PartialEq)]
4324pub struct StructField {
4325 pub name: String,
4327 pub field_type: Type,
4329}
4330
4331impl StructField {
4332 pub fn new(name: String, field_type: Type) -> Self {
4334 Self { name, field_type }
4335 }
4336
4337 pub fn name(&self) -> &str {
4339 &self.name
4340 }
4341
4342 pub fn is_function_pointer(&self) -> bool {
4344 matches!(self.field_type, Type::FunctionPointer { .. })
4345 }
4346}
4347
4348#[derive(Debug, Clone, PartialEq)]
4350pub struct Struct {
4351 pub name: String,
4353 pub fields: Vec<StructField>,
4355}
4356
4357impl Struct {
4358 pub fn new(name: String, fields: Vec<StructField>) -> Self {
4360 Self { name, fields }
4361 }
4362
4363 pub fn name(&self) -> &str {
4365 &self.name
4366 }
4367
4368 pub fn fields(&self) -> &[StructField] {
4370 &self.fields
4371 }
4372}
4373
4374#[derive(Debug, Clone, PartialEq)]
4376pub struct Variable {
4377 name: String,
4379 var_type: Type,
4381 initializer: Option<Expression>,
4383 is_static: bool,
4385 is_extern: bool,
4387 is_const: bool,
4389}
4390
4391impl Variable {
4392 pub fn new(name: String, var_type: Type) -> Self {
4394 Self {
4395 name,
4396 var_type,
4397 initializer: None,
4398 is_static: false,
4399 is_extern: false,
4400 is_const: false,
4401 }
4402 }
4403
4404 pub fn new_with_initializer(name: String, var_type: Type, initializer: Expression) -> Self {
4406 Self {
4407 name,
4408 var_type,
4409 initializer: Some(initializer),
4410 is_static: false,
4411 is_extern: false,
4412 is_const: false,
4413 }
4414 }
4415
4416 pub fn new_with_storage_class(
4418 name: String,
4419 var_type: Type,
4420 initializer: Option<Expression>,
4421 is_static: bool,
4422 is_extern: bool,
4423 is_const: bool,
4424 ) -> Self {
4425 Self {
4426 name,
4427 var_type,
4428 initializer,
4429 is_static,
4430 is_extern,
4431 is_const,
4432 }
4433 }
4434
4435 pub fn name(&self) -> &str {
4437 &self.name
4438 }
4439
4440 pub fn var_type(&self) -> &Type {
4442 &self.var_type
4443 }
4444
4445 pub fn is_function_pointer(&self) -> bool {
4447 matches!(self.var_type, Type::FunctionPointer { .. })
4448 }
4449
4450 pub fn function_pointer_param_count(&self) -> usize {
4452 match &self.var_type {
4453 Type::FunctionPointer { param_types, .. } => param_types.len(),
4454 _ => 0,
4455 }
4456 }
4457
4458 pub fn function_pointer_has_void_return(&self) -> bool {
4460 match &self.var_type {
4461 Type::FunctionPointer { return_type, .. } => matches!(**return_type, Type::Void),
4462 _ => false,
4463 }
4464 }
4465
4466 pub fn is_string_literal(&self) -> bool {
4478 let is_char_ptr =
4480 matches!(self.var_type, Type::Pointer(ref inner) if **inner == Type::Char);
4481
4482 if let Some(initializer) = &self.initializer {
4484 is_char_ptr && matches!(initializer, Expression::StringLiteral(_))
4485 } else {
4486 false
4487 }
4488 }
4489
4490 pub fn is_string_buffer(&self) -> bool {
4500 let is_char_ptr =
4502 matches!(self.var_type, Type::Pointer(ref inner) if **inner == Type::Char);
4503
4504 if let Some(Expression::FunctionCall { function, .. }) = &self.initializer {
4506 is_char_ptr && (function == "malloc" || function == "calloc")
4507 } else {
4508 false
4509 }
4510 }
4511
4512 pub fn initializer(&self) -> Option<&Expression> {
4516 self.initializer.as_ref()
4517 }
4518
4519 pub fn is_static(&self) -> bool {
4521 self.is_static
4522 }
4523
4524 pub fn is_extern(&self) -> bool {
4526 self.is_extern
4527 }
4528
4529 pub fn is_const(&self) -> bool {
4531 self.is_const
4532 }
4533}
4534
4535#[derive(Debug, Clone, PartialEq)]
4537pub struct Ast {
4538 functions: Vec<Function>,
4539 typedefs: Vec<Typedef>,
4540 structs: Vec<Struct>,
4541 macros: Vec<MacroDefinition>,
4542 variables: Vec<Variable>,
4543}
4544
4545#[derive(Debug, Clone, PartialEq)]
4575pub struct MacroDefinition {
4576 pub name: String,
4578 pub parameters: Vec<String>,
4580 pub body: String,
4582}
4583
4584impl MacroDefinition {
4585 pub fn new_object_like(name: String, body: String) -> Self {
4587 Self {
4588 name,
4589 parameters: vec![],
4590 body,
4591 }
4592 }
4593
4594 pub fn new_function_like(name: String, parameters: Vec<String>, body: String) -> Self {
4596 Self {
4597 name,
4598 parameters,
4599 body,
4600 }
4601 }
4602
4603 pub fn name(&self) -> &str {
4605 &self.name
4606 }
4607
4608 pub fn parameters(&self) -> &[String] {
4610 &self.parameters
4611 }
4612
4613 pub fn body(&self) -> &str {
4615 &self.body
4616 }
4617
4618 pub fn is_function_like(&self) -> bool {
4620 !self.parameters.is_empty()
4621 }
4622
4623 pub fn is_object_like(&self) -> bool {
4625 self.parameters.is_empty()
4626 }
4627}
4628
4629impl Ast {
4630 pub fn new() -> Self {
4632 Self {
4633 functions: Vec::new(),
4634 typedefs: Vec::new(),
4635 structs: Vec::new(),
4636 macros: Vec::new(),
4637 variables: Vec::new(),
4638 }
4639 }
4640
4641 pub fn functions(&self) -> &[Function] {
4643 &self.functions
4644 }
4645
4646 pub fn add_function(&mut self, function: Function) {
4648 self.functions.push(function);
4649 }
4650
4651 pub fn typedefs(&self) -> &[Typedef] {
4653 &self.typedefs
4654 }
4655
4656 pub fn add_typedef(&mut self, typedef: Typedef) {
4658 self.typedefs.push(typedef);
4659 }
4660
4661 pub fn structs(&self) -> &[Struct] {
4663 &self.structs
4664 }
4665
4666 pub fn add_struct(&mut self, struct_def: Struct) {
4669 if !self.structs.iter().any(|s| s.name() == struct_def.name()) {
4671 self.structs.push(struct_def);
4672 }
4673 }
4674
4675 pub fn macros(&self) -> &[MacroDefinition] {
4677 &self.macros
4678 }
4679
4680 pub fn add_macro(&mut self, macro_def: MacroDefinition) {
4682 self.macros.push(macro_def);
4683 }
4684
4685 pub fn variables(&self) -> &[Variable] {
4687 &self.variables
4688 }
4689
4690 pub fn add_variable(&mut self, variable: Variable) {
4692 self.variables.push(variable);
4693 }
4694}
4695
4696impl Default for Ast {
4697 fn default() -> Self {
4698 Self::new()
4699 }
4700}
4701
4702#[derive(Debug, Clone, PartialEq)]
4704pub struct Function {
4705 pub name: String,
4707 pub return_type: Type,
4709 pub parameters: Vec<Parameter>,
4711 pub body: Vec<Statement>,
4713}
4714
4715impl Function {
4716 pub fn new(name: String, return_type: Type, parameters: Vec<Parameter>) -> Self {
4718 Self {
4719 name,
4720 return_type,
4721 parameters,
4722 body: Vec::new(),
4723 }
4724 }
4725
4726 pub fn new_with_body(
4728 name: String,
4729 return_type: Type,
4730 parameters: Vec<Parameter>,
4731 body: Vec<Statement>,
4732 ) -> Self {
4733 Self {
4734 name,
4735 return_type,
4736 parameters,
4737 body,
4738 }
4739 }
4740}
4741
4742#[derive(Debug, Clone, PartialEq)]
4744#[allow(clippy::enum_variant_names)] pub enum Type {
4746 Void,
4748 Int,
4750 UnsignedInt,
4752 Float,
4754 Double,
4756 Char,
4758 Pointer(Box<Type>),
4760 Struct(String),
4762 FunctionPointer {
4764 param_types: Vec<Type>,
4766 return_type: Box<Type>,
4768 },
4769 Array {
4772 element_type: Box<Type>,
4774 size: Option<i64>,
4776 },
4777 TypeAlias(String),
4780}
4781
4782#[derive(Debug, Clone, PartialEq)]
4784pub struct Parameter {
4785 pub name: String,
4787 pub param_type: Type,
4789 pub is_pointee_const: bool,
4792}
4793
4794impl Parameter {
4795 pub fn new(name: String, param_type: Type) -> Self {
4797 Self {
4798 name,
4799 param_type,
4800 is_pointee_const: false,
4801 }
4802 }
4803
4804 pub fn new_with_const(name: String, param_type: Type, is_pointee_const: bool) -> Self {
4807 Self {
4808 name,
4809 param_type,
4810 is_pointee_const,
4811 }
4812 }
4813
4814 pub fn is_function_pointer(&self) -> bool {
4816 matches!(self.param_type, Type::FunctionPointer { .. })
4817 }
4818
4819 pub fn is_const_char_pointer(&self) -> bool {
4824 self.is_pointee_const
4825 && matches!(self.param_type, Type::Pointer(ref inner) if matches!(**inner, Type::Char))
4826 }
4827
4828 pub fn is_char_pointer(&self) -> bool {
4830 matches!(self.param_type, Type::Pointer(ref inner) if matches!(**inner, Type::Char))
4831 }
4832}
4833
4834#[cfg(test)]
4835#[path = "parser_tests.rs"]
4836mod parser_tests;
4837
4838#[cfg(test)]
4839#[path = "pointer_arithmetic_tests.rs"]
4840mod pointer_arithmetic_tests;
4841
4842#[cfg(test)]
4843#[path = "break_continue_tests.rs"]
4844mod break_continue_tests;