translate_collect.odin (26432B)
1 #+private file 2 #+feature dynamic-literals 3 package bindgen2 4 5 import clang "../libclang" 6 import "core:slice" 7 import "core:log" 8 import "core:strings" 9 import "core:strconv" 10 import "core:unicode" 11 import "core:unicode/utf8" 12 import "core:fmt" 13 14 @(private="package") 15 Translate_Collect_Result :: struct { 16 source: string, 17 extra_imports: []string, 18 macros: []Raw_Macro, 19 } 20 21 // Parses the C headers and "collects" the things we need from them. This will create a bunch types 22 // and declarations in the `Translate_State` struct. This file avoids doing any furher processing, 23 // that is deferred to `translate_process`. 24 @(private="package", require_results) 25 translate_collect :: proc(filename: string, config: Config, types: Type_List, decls: Decl_List) -> (Translate_Collect_Result, bool) { 26 clang_version := string_from_clang_string(clang.getClangVersion()) 27 clang_version = strings.trim_prefix(clang_version, "clang version ") 28 clang_version_major_end := strings.index_rune(clang_version, '.') 29 30 if clang_version_major_end == -1 { 31 log.panic("Failed checking libclang version") 32 } 33 34 clang_major_version_str := clang_version[:clang_version_major_end] 35 36 if clang_major_version, clang_major_version_ok := strconv.parse_int(clang_major_version_str); 37 clang_major_version_ok && clang_major_version < 16 { 38 log.panic("libclang version 16 or newer is required") 39 } 40 41 clang_args: [dynamic]cstring 42 append(&clang_args, "-fparse-all-comments") 43 44 for &include in config.clang_include_paths { 45 append(&clang_args, fmt.ctprintf("-I%v", include)) 46 } 47 48 for k, v in config.clang_defines { 49 append(&clang_args, fmt.ctprintf("-D%s=%s", k, v)) 50 } 51 52 // Clang uses 1 and 0 instead of true and false. The index is a set of translation units. 53 // 54 // TODO: Should all bindings created into a single directory use the same index, so they can 55 // see things between them? 56 index := clang.createIndex(1, 0) 57 58 unit: clang.Translation_Unit 59 60 options: clang.Translation_Unit_Flags = { 61 .DetailedPreprocessingRecord, // Keep macros. 62 .SkipFunctionBodies, 63 .KeepGoing, // Keep going on errors. 64 } 65 66 filename_cstr := to_cstring(filename) 67 68 err := clang.parseTranslationUnit2( 69 index, 70 filename_cstr, 71 raw_data(clang_args), 72 i32(len(clang_args)), 73 nil, 74 0, 75 options, 76 &unit, 77 ) 78 79 if err != .Success { 80 log.errorf("Failed to parse translation unit for %s. Error code: %v", filename, err) 81 return {}, false 82 } 83 84 file := clang.getFile(unit, filename_cstr) 85 root_cursor := clang.getTranslationUnitCursor(unit) 86 source_size: uint 87 source := clang.getFileContents(unit, file, &source_size) 88 89 tcs := Translate_Collect_State { 90 source = strings.string_from_ptr((^u8)(source), int(source_size)), 91 translation_unit = unit, 92 types = types, 93 decls = decls, 94 } 95 96 // I dislike visitors. They make the code hard to read. So I build a map of all parents and 97 // children. That way we can use this lookup to find arrays of children and iterate them normally. 98 build_cursor_children_lookup(root_cursor, &tcs.children_lookup) 99 100 root_children := tcs.children_lookup[root_cursor] 101 102 for c in root_children { 103 loc := get_cursor_location(c) 104 105 if clang.File_isEqual(file, loc.file) == 0 { 106 continue 107 } 108 109 create_declaration(c, &tcs) 110 } 111 112 extra_imports, extra_imports_err := slice.map_keys(tcs.extra_imports) 113 assert(extra_imports_err == nil) 114 115 return { 116 source = tcs.source, 117 extra_imports = extra_imports, 118 macros = tcs.macros[:], 119 }, true 120 } 121 122 Cursor_Children_Map :: map[clang.Cursor][]clang.Cursor 123 124 Translate_Collect_State :: struct { 125 decls: Decl_List, 126 type_lookup: map[clang.Type]Type_Index, 127 types: Type_List, 128 children_lookup: Cursor_Children_Map, 129 source: string, 130 extra_imports: map[string]bool, 131 macros: [dynamic]Raw_Macro, 132 translation_unit: clang.Translation_Unit, 133 } 134 135 build_cursor_children_lookup :: proc(c: clang.Cursor, res: ^Cursor_Children_Map) { 136 Build_Children_State :: struct { 137 res: ^Cursor_Children_Map, 138 children: [dynamic]clang.Cursor, 139 } 140 141 bcs := Build_Children_State { 142 res = res, 143 } 144 145 clang.visitChildren(c, curstor_iterator_iterate, &bcs) 146 147 curstor_iterator_iterate: clang.Cursor_Visitor : proc "c" ( 148 cursor, parent: clang.Cursor, 149 state: clang.Client_Data, 150 ) -> clang.Child_Visit_Result { 151 context = gen_ctx 152 bcs := (^Build_Children_State)(state) 153 append(&bcs.children, cursor) 154 build_cursor_children_lookup(cursor, bcs.res) 155 return .Continue 156 } 157 158 res[c] = bcs.children[:] 159 } 160 161 create_declaration :: proc(c: clang.Cursor, tcs: ^Translate_Collect_State) { 162 if clang.Cursor_isAnonymous(c) == 1 && c.kind != .EnumDecl { 163 return 164 } 165 166 name := get_cursor_name(c) 167 comment_before := string_from_clang_string(clang.Cursor_getRawCommentText(c)) 168 line := get_cursor_location(c).line 169 is_forward_declare := clang.isCursorDefinition(c) == 0 170 171 side_comment: string 172 side_comment_align_whitespace: int 173 { 174 source_range := clang.getCursorExtent(c) 175 176 start := clang.getRangeStart(source_range) 177 start_offset: u32 178 clang.getExpansionLocation(start, nil, nil, nil, &start_offset) 179 end := clang.getRangeEnd(source_range) 180 end_offset: u32 181 clang.getExpansionLocation(end, nil, nil, nil, &end_offset) 182 side_comment, side_comment_align_whitespace = find_comment_at_line_end(tcs.source[start_offset:]) 183 } 184 185 ct := clang.getCursorType(c) 186 187 #partial switch c.kind { 188 // Struct and union is the same, only difference is that the `Type_Struct` will get `raw_union` 189 // set to true. 190 case .StructDecl, .UnionDecl: 191 ti := create_type_recursive(ct, tcs) 192 193 if ti == TYPE_INDEX_NONE { 194 log.errorf("Unknown type: %v", ct) 195 return 196 } 197 198 add_decl(tcs.decls, { 199 comment_before = comment_before, 200 def = ti, 201 name = name, 202 original_line = line, 203 side_comment = side_comment, 204 is_forward_declare = is_forward_declare, 205 }) 206 207 children := tcs.children_lookup[c] 208 209 for cc in children { 210 create_declaration(cc, tcs) 211 } 212 213 case .TypedefDecl: 214 ti := create_type_recursive(ct, tcs) 215 216 if ti == TYPE_INDEX_NONE { 217 log.errorf("Unknown type: %v", ct) 218 return 219 } 220 221 add_decl(tcs.decls, { 222 comment_before = comment_before, 223 def = ti, 224 name = name, 225 original_line = line, 226 side_comment = side_comment, 227 is_forward_declare = is_forward_declare, 228 }) 229 230 case .EnumDecl: 231 ti := create_type_recursive(ct, tcs) 232 233 if ti == TYPE_INDEX_NONE { 234 log.errorf("Unknown type: %v", ct) 235 return 236 } 237 238 if clang.Cursor_isAnonymous(c) == 1 { 239 e, is_enum := tcs.types[ti].(Type_Enum) 240 241 if is_enum { 242 for &m in e.members { 243 add_decl(tcs.decls, { 244 name = m.name, 245 def = Fixed_Value(fmt.tprint(m.value)), 246 original_line = line, 247 248 // It's not really from a macro, but it's probably best if it behaves as if. 249 from_macro = true, 250 }) 251 } 252 } 253 return 254 } 255 256 add_decl(tcs.decls, { 257 comment_before = comment_before, 258 def = ti, 259 name = name, 260 original_line = line, 261 side_comment = side_comment, 262 is_forward_declare = is_forward_declare, 263 }) 264 265 case .FunctionDecl: 266 if clang.Cursor_isFunctionInlined(c) == 1 { 267 return 268 } 269 270 ti := create_proc_type(tcs.children_lookup[c], ct, tcs) 271 272 if ti == TYPE_INDEX_NONE { 273 log.errorf("Unknown type: %v", ct) 274 return 275 } 276 277 add_decl(tcs.decls, { 278 comment_before = comment_before, 279 def = ti, 280 name = name, 281 original_line = line, 282 side_comment = side_comment, 283 is_forward_declare = is_forward_declare, 284 }) 285 286 case .MacroDefinition: 287 if clang.Cursor_isMacroBuiltin(c) == 1 { 288 return 289 } 290 291 source_range := clang.getCursorExtent(c) 292 293 start := clang.getRangeStart(source_range) 294 start_offset: u32 295 clang.getExpansionLocation(start, nil, nil, nil, &start_offset) 296 end := clang.getRangeEnd(source_range) 297 end_offset: u32 298 clang.getExpansionLocation(end, nil, nil, nil, &end_offset) 299 macro_source := tcs.source[start_offset:end_offset] 300 301 whitespace_after_name: int 302 first_space_seen := false 303 name_end: int 304 305 for c, i in macro_source { 306 if unicode.is_white_space(c) { 307 if !first_space_seen { 308 first_space_seen = true 309 name_end = i 310 } 311 312 whitespace_after_name += 1 313 } else { 314 if first_space_seen { 315 break 316 } 317 } 318 } 319 320 comment := find_comment_before(tcs.source, '#', int(start_offset)) 321 322 clang_tokens: [^]clang.Token 323 clang_token_count: u32 324 clang.tokenize(tcs.translation_unit, source_range, &clang_tokens, &clang_token_count) 325 326 if clang_token_count > 1 { 327 tokens := make([]Raw_Macro_Token, clang_token_count - 1) 328 329 for i in 1..<clang_token_count { 330 val := string_from_clang_string(clang.getTokenSpelling(tcs.translation_unit, clang_tokens[i])) 331 kind: Raw_Macro_Token_Kind 332 333 #partial switch clang.getTokenKind(clang_tokens[i]) { 334 case .Punctuation: kind = .Punctuation 335 case .Keyword: kind = .Keyword 336 case .Identifier: kind = .Identifier 337 case .Literal: kind = .Literal 338 } 339 340 tokens[i - 1] = { 341 value = val, 342 kind = kind, 343 } 344 } 345 346 append(&tcs.macros, Raw_Macro { 347 name = name, 348 is_function_like = clang.Cursor_isMacroFunctionLike(c) == 1, 349 tokens = tokens, 350 comment = comment, 351 side_comment = side_comment, 352 whitespace_before_side_comment = side_comment_align_whitespace, 353 whitespace_after_name = whitespace_after_name, 354 original_line = line, 355 }) 356 } 357 } 358 } 359 360 find_comment_before :: proc(src: string, start_rune: rune, start_offset: int) -> string { 361 Find_Comment_State :: enum { 362 Looking_For_Start, 363 Looking_For_Comment, 364 Looking_For_Single_Line_Start, 365 Verifying_Single_Line, 366 Inside_Block_Comment, 367 } 368 369 find_state: Find_Comment_State 370 comment_start := -1 371 comment_end: int 372 373 comment_loop: for i := start_offset; i >= 0; { 374 c := utf8.rune_at(src, i) 375 defer i -= utf8.rune_size(c) 376 switch find_state { 377 case .Looking_For_Start: 378 if c == start_rune { 379 comment_end = i 380 find_state = .Looking_For_Comment 381 break 382 } 383 384 if c == '\n' { 385 break comment_loop 386 } 387 case .Looking_For_Comment: 388 if unicode.is_white_space(c) { 389 break 390 } 391 392 if c == '/' && i > 1 && src[i - 1] == '*' { 393 find_state = .Inside_Block_Comment 394 break 395 } 396 397 // TODO: Special case when line only is `//` 398 399 find_state = .Looking_For_Single_Line_Start 400 case .Looking_For_Single_Line_Start: 401 if c == '\n' { 402 break comment_loop 403 } 404 405 if c == '/' && i < len(src) - 1 && src[i + 1] == '/' { 406 find_state = .Verifying_Single_Line 407 break 408 } 409 410 case .Verifying_Single_Line: 411 if c == '\n' { 412 comment_start = i 413 find_state = .Looking_For_Comment 414 break 415 } 416 417 if c == '/' && ((i > 0 && src[i - 1] == '/') || (i < len(src)-1 && src[i + 1] == '/')) { 418 break 419 } 420 421 if !unicode.is_white_space(c) { 422 break comment_loop 423 } 424 case .Inside_Block_Comment: 425 if c == '/' && i < len(src) - 1 && src[i + 1] == '*' { 426 find_state = .Verifying_Single_Line 427 break 428 } 429 } 430 } 431 432 if comment_start != -1 && comment_end > comment_start { 433 return strings.trim_space(src[comment_start:comment_end]) 434 } 435 436 return "" 437 } 438 439 find_comment_at_line_end :: proc(str: string) -> (string, int) { 440 space_before_comment: int 441 comment_start: int 442 block_comment: bool 443 444 for c, i in str { 445 if c == ' ' { 446 space_before_comment += 1 447 } else if c == '/' && i + 1 < len(str) && str[i + 1] == '/' { 448 comment_start = i 449 break 450 } else if c == '/' && i + 1 < len(str) && str[i + 1] == '*' { 451 comment_start = i 452 block_comment = true 453 break 454 } else if c == '\n' { 455 break 456 } else { 457 space_before_comment = 0 458 } 459 } 460 461 if comment_start == 0 { 462 return "", 0 463 } 464 465 if block_comment { 466 from_start := str[comment_start:] 467 468 for c, i in from_start { 469 if c == '*' && i < len(from_start) - 1 && from_start[i + 1] == '/' { 470 return from_start[:i+2], space_before_comment 471 } 472 } 473 } else { 474 from_start := str[comment_start:] 475 476 for c, i in from_start { 477 if c == '\n' { 478 return from_start[:i], space_before_comment 479 } 480 } 481 } 482 483 return "", 0 484 } 485 486 type_probably_is_cstring :: proc(ct: clang.Type) -> bool { 487 if ct.kind != .Pointer { 488 return false 489 } 490 491 pt := clang.getPointeeType(ct) 492 493 return (pt.kind == .Char_S || pt.kind == .SChar) 494 } 495 496 get_type_name_or_create_anon_type :: proc(ct: clang.Type, tcs: ^Translate_Collect_State) -> Definition { 497 #partial switch ct.kind { 498 case .Void: 499 return Fixed_Value("struct {}") 500 case .Bool: 501 return Fixed_Value("bool") 502 case .Char_U, .UChar: 503 return Fixed_Value("u8") 504 case .UShort: 505 return Fixed_Value("u16") 506 case .UInt: 507 return Fixed_Value("u32") 508 case .ULong: 509 tcs.extra_imports["core:c"] = true 510 return Fixed_Value("c.ulong") 511 case .ULongLong: 512 return Fixed_Value("u64") 513 case .UInt128: 514 return Fixed_Value("u128") 515 case .Char_S, .SChar: 516 return Fixed_Value("i8") 517 case .Short: 518 return Fixed_Value("i16") 519 case .Int: 520 return Fixed_Value("i32") 521 case .Long: 522 tcs.extra_imports["core:c"] = true 523 return Fixed_Value("c.long") 524 case .LongLong: 525 return Fixed_Value("i64") 526 case .Int128: 527 return Fixed_Value("i128") 528 case .Float: 529 return Fixed_Value("f32") 530 case .Double, .LongDouble: 531 return Fixed_Value("f64") 532 case .NullPtr: 533 return Fixed_Value("rawptr") 534 case .WChar: 535 tcs.extra_imports["core:c"] = true 536 return Fixed_Value("c.wchar_t") 537 538 case .Record, .Enum: 539 ctc := clang.getTypeDeclaration(ct) 540 if clang.Cursor_isAnonymous(ctc) == 0 { 541 return Type_Name(get_cursor_name(ctc)) 542 } 543 544 case .Typedef: 545 ctc := clang.getTypeDeclaration(ct) 546 if clang.Cursor_isAnonymous(ctc) == 0 { 547 name := get_cursor_name(ctc) 548 549 if replacement, has_replacement := c_type_mapping[name]; has_replacement { 550 if strings.has_prefix(replacement, "c.") { 551 tcs.extra_imports["core:c"] = true 552 } else if strings.has_prefix(replacement, "libc.") { 553 tcs.extra_imports["core:c/libc"] = true 554 } if strings.has_prefix(replacement, "posix.") { 555 tcs.extra_imports["core:sys/posix"] = true 556 } 557 return Fixed_Value(replacement) 558 } 559 560 return Type_Name(name) 561 } 562 563 case .Elaborated: 564 return get_type_name_or_create_anon_type(clang.Type_getNamedType(ct), tcs) 565 } 566 567 // No name found! Create a real type definition (used by anonymous types etc) 568 return create_type_recursive(ct, tcs) 569 } 570 571 is_fixed_array :: proc(ct: clang.Type) -> bool { 572 ct := ct 573 574 if ct.kind == .Elaborated { 575 ct = clang.Type_getNamedType(ct) 576 } 577 578 if ct.kind == .ConstantArray { 579 return true 580 } 581 582 if ct.kind == .Typedef { 583 underlying := clang.getTypedefDeclUnderlyingType(clang.getTypeDeclaration(ct)) 584 585 if underlying.kind == .ConstantArray { 586 return true 587 } 588 } 589 590 return false 591 } 592 593 // This is a separate proc because we call it both from create_type_recursive and from 594 // create_declaration. It's used in create_declaration so we get a unique proc type per proc. 595 // Otherweise the FunctionProto stuff may make it so that ther are shared proc types, which will 596 // break stuff. 597 create_proc_type :: proc(param_childs: []clang.Cursor, ct: clang.Type, tcs: ^Translate_Collect_State) -> Type_Index { 598 proc_type := reserve_type(ct, tcs) 599 params: [dynamic]Type_Procedure_Parameter 600 601 if len(param_childs) > 0 { 602 for child in param_childs { 603 if child.kind != .ParmDecl { 604 continue 605 } 606 607 param_type := clang.getCursorType(child) 608 name := get_cursor_name(child) 609 610 type_id: Definition 611 612 if unwrapped_type, is_proc := unwrap_proc_pointers(param_type); is_proc { 613 type_id = create_proc_type(tcs.children_lookup[child], unwrapped_type, tcs) 614 } else { 615 type_id = get_type_name_or_create_anon_type(unwrapped_type, tcs) 616 617 // Fixed arrays are passed by pointer into procs. That's how it works in C. I.e. 618 // `float numbers[2]` as a function parameter is equivalent to `float *numbers`, but 619 // you have that `2` there for documentation purposes. So by default we turn such 620 // a parameter into `numbers: ^[2]f32`. 621 if is_fixed_array(param_type) { 622 wrapper_idx := Type_Index(len(tcs.types)) 623 append_nothing(tcs.types) 624 tcs.types[wrapper_idx] = Type_Pointer { 625 pointed_to_type = type_id, 626 } 627 type_id = wrapper_idx 628 } 629 } 630 631 append(¶ms, Type_Procedure_Parameter { 632 name = name, 633 type = type_id, 634 }) 635 } 636 } else { 637 num_args := clang.getNumArgTypes(ct) 638 for i in 0..<num_args { 639 param_type := clang.getArgType(ct, u32(i)) 640 641 append(¶ms, Type_Procedure_Parameter { 642 type = get_type_name_or_create_anon_type(param_type, tcs), 643 }) 644 } 645 } 646 647 result_ct := clang.getResultType(ct) 648 result_type_id: Definition 649 650 if result_ct.kind != .Void { 651 result_type_id = get_type_name_or_create_anon_type(result_ct, tcs) 652 } 653 654 calling_conv := Calling_Convention.C 655 656 #partial switch clang.getFunctionTypeCallingConv(ct) { 657 case .X86StdCall: 658 calling_conv = .Std_Call 659 case .X86FastCall: 660 calling_conv = .Fast_Call 661 } 662 663 type_definition := Type_Procedure { 664 parameters = params[:], 665 result_type = result_type_id, 666 calling_convention = calling_conv, 667 668 // Zero length params and variadic isn't really a usable combination. Just pretend it isn't 669 // variadic in that case. 670 is_variadic = len(params) > 0 && clang.isFunctionTypeVariadic(ct) == 1, 671 } 672 673 tcs.types[proc_type] = type_definition 674 return proc_type 675 } 676 677 reserve_type :: proc(ct: clang.Type, tcs: ^Translate_Collect_State) -> Type_Index { 678 idx := Type_Index(len(tcs.types)) 679 append_nothing(tcs.types) 680 tcs.type_lookup[ct] = idx 681 return idx 682 } 683 684 // In Odin, every proc is a pointer, and it is like that in C bindings too. So if something takes a 685 // ptr to a func in C, then it should just take a proc in Odin. In other words, we need to bypass 686 // one level of pointers whenever the thing we are looking at ends in a function. 687 unwrap_proc_pointers :: proc(t: clang.Type) -> (unwrapped_type: clang.Type, is_proc: bool) { 688 if t.kind == .Pointer { 689 first_pointee := clang.getPointeeType(t) 690 pointee := first_pointee 691 692 // We loop here so 'some_func_type**' just becomes 'some_func_type*'. We need to find if the 693 // chain of pointers end i function type. But we need to discard the first level of pointer 694 // indirection. 695 for pointee.kind != .Invalid { 696 if pointee.kind == .FunctionProto || pointee.kind == .FunctionNoProto { 697 return first_pointee, true 698 } else if pointee.kind == .Elaborated { 699 named := clang.Type_getNamedType(pointee) 700 701 if named.kind == .FunctionProto || named.kind == .FunctionNoProto { 702 return first_pointee, true 703 } else if named.kind == .Typedef { 704 underlying := clang.getTypedefDeclUnderlyingType(clang.getTypeDeclaration(pointee)) 705 706 if underlying.kind == .FunctionProto || underlying.kind == .FunctionNoProto { 707 return first_pointee, false 708 } 709 } 710 } 711 712 pointee = clang.getPointeeType(pointee) 713 } 714 } 715 716 return t, (t.kind == .FunctionProto || t.kind == .FunctionNoProto) 717 } 718 719 create_type_recursive :: proc(ct: clang.Type, tcs: ^Translate_Collect_State) -> Type_Index { 720 if t_idx, has_t_idx := tcs.type_lookup[ct]; has_t_idx { 721 return t_idx 722 } 723 724 add_anonymous_type :: proc(t: Type, types: ^[dynamic]Type) -> Type_Index { 725 idx := Type_Index(len(types)) 726 append(types, t) 727 return idx 728 } 729 730 to_add: Maybe(Type) 731 732 #partial switch ct.kind { 733 case .Pointer: 734 clang_pointee_type := clang.getPointeeType(ct) 735 736 if clang_pointee_type.kind == .Void { 737 to_add = Type_Raw_Pointer{} 738 } else if type_probably_is_cstring(ct) { 739 to_add = Type_CString{} 740 } else if clang_pointee_type.kind == .FunctionProto { 741 return create_proc_type(tcs.children_lookup[clang.getTypeDeclaration(clang_pointee_type)], clang_pointee_type, tcs) 742 } else { 743 ptr_type_idx := reserve_type(ct, tcs) 744 pointing_to_id := get_type_name_or_create_anon_type(clang_pointee_type, tcs) 745 tcs.types[ptr_type_idx] = Type_Pointer { pointed_to_type = pointing_to_id } 746 return ptr_type_idx 747 } 748 case .Record: 749 c := clang.getTypeDeclaration(ct) 750 struct_type_idx := reserve_type(ct, tcs) 751 struct_children := tcs.children_lookup[c] 752 fields: [dynamic]Type_Struct_Field 753 prev_named_field := -1 754 755 for sc in struct_children { 756 sc_kind := clang.getCursorKind(sc) 757 758 #partial switch sc_kind { 759 case .FieldDecl: 760 sct := clang.getCursorType(sc) 761 type_id: Definition 762 763 if unwrapped_type, is_proc := unwrap_proc_pointers(sct); is_proc { 764 type_id = create_proc_type(tcs.children_lookup[sc], unwrapped_type, tcs) 765 } else { 766 type_id = get_type_name_or_create_anon_type(unwrapped_type, tcs) 767 } 768 769 name := get_cursor_name(sc) 770 771 if type_id == nil { 772 log.errorf("Unresolved struct field type: %v", sc) 773 } 774 775 field_loc := get_cursor_location(sc) 776 777 comment_before := find_comment_before(tcs.source, '\n', field_loc.offset) 778 comment_on_right, _ := find_comment_at_line_end(tcs.source[field_loc.offset:]) 779 780 if prev_named_field >= 0 && prev_named_field == len(fields) - 1 && 781 fields[prev_named_field].type == type_id && field_loc.line == fields[prev_named_field].line { 782 append(&fields[prev_named_field].names, name) 783 } else { 784 prev_named_field = len(fields) 785 append(&fields, Type_Struct_Field { 786 names = [dynamic]string { name }, 787 type = type_id, 788 comment_before = comment_before, 789 comment_on_right = comment_on_right, 790 line = field_loc.line, 791 }) 792 } 793 794 case .StructDecl, .UnionDecl: 795 if clang.Cursor_isAnonymousRecordDecl(sc) == 1 { 796 sct := clang.getCursorType(sc) 797 type_id := get_type_name_or_create_anon_type(sct, tcs) 798 799 field_loc := get_cursor_location(sc) 800 comment_loc := get_comment_location(sc) 801 802 comment := string_from_clang_string(clang.Cursor_getRawCommentText(sc)) 803 comment_before: string 804 comment_on_right: string 805 806 if field_loc.line == comment_loc.line { 807 comment_on_right = comment 808 } else { 809 comment_before = comment 810 } 811 812 append(&fields, Type_Struct_Field { 813 anonymous = true, 814 type = type_id, 815 comment_before = comment_before, 816 comment_on_right = comment_on_right, 817 }) 818 } 819 } 820 } 821 822 type_definition := Type_Struct { 823 fields = fields[:], 824 raw_union = c.kind == .UnionDecl, 825 } 826 827 tcs.types[struct_type_idx] = type_definition 828 829 return struct_type_idx 830 case .Enum: 831 enum_type_idx := reserve_type(ct, tcs) 832 c := clang.getTypeDeclaration(ct) 833 enum_children := tcs.children_lookup[c] 834 members: [dynamic]Type_Enum_Member 835 backing_type := clang.getEnumDeclIntegerType(c) 836 is_unsigned_type := backing_type.kind >= .Char_U && backing_type.kind <= .UInt128 837 838 for ec in enum_children { 839 member_name := get_cursor_name(ec) 840 value := is_unsigned_type ? int(clang.getEnumConstantDeclUnsignedValue(ec)) : int(clang.getEnumConstantDeclValue(ec)) 841 cursor_loc := get_cursor_location(ec) 842 843 comment_before := find_comment_before(tcs.source, '\n', cursor_loc.offset) 844 comment_on_right, _ := find_comment_at_line_end(tcs.source[cursor_loc.offset:]) 845 846 append(&members, Type_Enum_Member { 847 name = member_name, 848 value = value, 849 comment_before = comment_before, 850 comment_on_right = comment_on_right, 851 }) 852 } 853 854 storage_type: typeid = i32 855 856 #partial switch backing_type.kind { 857 case .Char_U: 858 storage_type = u8 859 case .UChar: 860 storage_type = u8 861 case .Char16: 862 storage_type = i16 863 case .Char32: 864 storage_type = i32 865 case .UShort: 866 storage_type = u16 867 case .UInt: 868 storage_type = u32 869 case .ULong: 870 storage_type = u32 871 case .ULongLong: 872 storage_type = u64 873 case .UInt128: 874 storage_type = u128 875 case .Char_S: 876 storage_type = i8 877 case .SChar: 878 storage_type = i8 879 case .Short: 880 storage_type = i16 881 case .Int: 882 storage_type = i32 883 case .Long: 884 storage_type = i32 885 case .LongLong: 886 storage_type = i64 887 case .Int128: 888 storage_type = i128 889 } 890 891 type_definition := Type_Enum { 892 storage_type = storage_type, 893 members = members[:], 894 } 895 896 tcs.types[enum_type_idx] = type_definition 897 return enum_type_idx 898 899 case .Elaborated: 900 // Just return the type index here so we "short circuit" past `struct S` etc 901 named_type := clang.Type_getNamedType(ct) 902 elaborated_type_idx := create_type_recursive(named_type, tcs) 903 tcs.type_lookup[ct] = elaborated_type_idx 904 return elaborated_type_idx 905 case .Typedef: 906 alias_type_idx := reserve_type(ct, tcs) 907 c := clang.getTypeDeclaration(ct) 908 underlying := clang.getTypedefDeclUnderlyingType(c) 909 type_id: Definition 910 911 if unwrapped_type, is_proc := unwrap_proc_pointers(underlying); is_proc { 912 type_id = create_proc_type(tcs.children_lookup[c], unwrapped_type, tcs) 913 } else { 914 type_id = get_type_name_or_create_anon_type(unwrapped_type, tcs) 915 } 916 917 type_definition := Type_Alias { 918 aliased_type = type_id, 919 } 920 921 tcs.types[alias_type_idx] = type_definition 922 923 return alias_type_idx 924 case .ConstantArray: 925 array_type_idx := reserve_type(ct, tcs) 926 clang_element_type := clang.getArrayElementType(ct) 927 928 type_definition := Type_Fixed_Array { 929 element_type = get_type_name_or_create_anon_type(clang_element_type, tcs), 930 size = int(clang.getArraySize(ct)), 931 } 932 933 tcs.types[array_type_idx] = type_definition 934 935 return array_type_idx 936 937 case .IncompleteArray: 938 array_type_idx := reserve_type(ct, tcs) 939 clang_element_type := clang.getArrayElementType(ct) 940 941 type_definition := Type_Multipointer { 942 pointed_to_type = get_type_name_or_create_anon_type(clang_element_type, tcs), 943 } 944 945 tcs.types[array_type_idx] = type_definition 946 947 return array_type_idx 948 949 case .FunctionProto, .FunctionNoProto: 950 return create_proc_type({}, ct, tcs) 951 } 952 953 if t, t_ok := to_add.?; t_ok { 954 idx := reserve_type(ct, tcs) 955 tcs.types[idx] = t 956 return idx 957 } 958 959 //log.error("Unknown type") 960 return TYPE_INDEX_NONE 961 } 962 963 get_cursor_name :: proc(cursor: clang.Cursor) -> string { 964 return string_from_clang_string(clang.getCursorSpelling(cursor)) 965 } 966 967 get_type_name :: proc(type: clang.Type) -> string { 968 return string_from_clang_string(clang.getTypeSpelling(type)) 969 } 970 971 string_from_clang_string :: proc(str: clang.String) -> string { 972 ret := strings.clone_from_cstring(clang.getCString(str)) 973 clang.disposeString(str) 974 return ret 975 } 976 977 Location :: struct { 978 file: clang.File, 979 offset: int, 980 line: int, 981 column: int, 982 } 983 984 get_cursor_location :: proc(cursor: clang.Cursor) -> Location { 985 file: clang.File 986 offset: u32 987 column: u32 988 line: u32 989 990 clang.getExpansionLocation(clang.getCursorLocation(cursor), &file, &line, &column, &offset) 991 992 return { 993 file = file, 994 offset = int(offset), 995 line = int(line), 996 column = int(column), 997 } 998 } 999 1000 get_comment_location :: proc(cursor: clang.Cursor) -> Location { 1001 file: clang.File 1002 offset: u32 1003 column: u32 1004 line: u32 1005 1006 clang.getExpansionLocation(clang.getRangeStart(clang.Cursor_getCommentRange(cursor)), &file, &line, &column, &offset) 1007 1008 return { 1009 file = file, 1010 offset = int(offset), 1011 line = int(line), 1012 column = int(column), 1013 } 1014 }