odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

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(&params, 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(&params, 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 }