c argument names in bindings

This commit is contained in:
2026-06-25 08:28:30 +02:00
parent 9d1130b359
commit c08bc6d0cc
9 changed files with 84 additions and 19 deletions
+1 -1
View File
@@ -758,7 +758,7 @@ validate_declarations :: proc(checker: ^Checker) {
"void is only valid as a function result type",
)
}
if contains_name(locals[:], param.name) {
if param.name != checker.sink_symbol && contains_name(locals[:], param.name) {
source.addf(
checker.diagnostics,
param.span,
+11 -6
View File
@@ -68,12 +68,13 @@ Alias :: struct {
}
Function :: struct {
name: string,
params: []Type_Id,
result: Type_Id,
variadic: bool,
link_name: string,
reason: string,
name: string,
params: []Type_Id,
param_names: []string,
result: Type_Id,
variadic: bool,
link_name: string,
reason: string,
}
// Trampoline is a generated C wrapper that gives an external symbol to a
@@ -172,6 +173,10 @@ destroy_result :: proc(result: ^Result) {
for function in result.functions {
delete(function.name, result.allocator)
delete(function.params, result.allocator)
for param_name in function.param_names {
delete(param_name, result.allocator)
}
delete(function.param_names, result.allocator)
delete(function.link_name, result.allocator)
delete(function.reason, result.allocator)
}
+15
View File
@@ -91,6 +91,8 @@ Api :: struct {
get_result_type: proc "c"(CXType) -> CXType,
get_num_arg_types: proc "c"(CXType) -> i32,
get_arg_type: proc "c"(CXType, u32) -> CXType,
cursor_get_num_arguments: proc "c"(CXCursor) -> i32,
cursor_get_argument: proc "c"(CXCursor, u32) -> CXCursor,
is_function_type_variadic: proc "c"(CXType) -> u32,
is_const_qualified_type: proc "c"(CXType) -> u32,
is_volatile_qualified_type: proc "c"(CXType) -> u32,
@@ -215,6 +217,8 @@ load_api_from :: proc(path: string) -> (Api, bool) {
load_proc(&api, "clang_getResultType", &api.get_result_type) &&
load_proc(&api, "clang_getNumArgTypes", &api.get_num_arg_types) &&
load_proc(&api, "clang_getArgType", &api.get_arg_type) &&
load_proc(&api, "clang_Cursor_getNumArguments", &api.cursor_get_num_arguments) &&
load_proc(&api, "clang_Cursor_getArgument", &api.cursor_get_argument) &&
load_proc(&api, "clang_isFunctionTypeVariadic", &api.is_function_type_variadic) &&
load_proc(&api, "clang_isConstQualifiedType", &api.is_const_qualified_type) &&
load_proc(&api, "clang_isVolatileQualifiedType", &api.is_volatile_qualified_type) &&
@@ -1371,6 +1375,9 @@ visit_cursor :: proc "c"(cursor, parent: CXCursor, client_data: rawptr) -> i32 {
}
params: [dynamic]Type_Id
params.allocator = ctx.allocator
param_names: [dynamic]string
param_names.allocator = ctx.allocator
named_count := ctx.api.cursor_get_num_arguments(cursor)
count := ctx.api.get_num_arg_types(function_type)
if count < 0 {
reason = "function declaration has no prototype"
@@ -1381,6 +1388,13 @@ visit_cursor :: proc "c"(cursor, parent: CXCursor, client_data: rawptr) -> i32 {
if param == INVALID_TYPE && len(reason) == 0 {
reason = "function parameter type is not supported"
}
// Parameter names come from the function cursor's argument cursors,
// not the function type. Absent (e.g. `int f(int, char*)`) -> "".
if i32(index) < named_count {
append(&param_names, clone_cx_string(ctx.api, ctx.api.get_cursor_spelling(ctx.api.cursor_get_argument(cursor, u32(index))), ctx.allocator))
} else {
append(&param_names, fmt.aprintf("", allocator=ctx.allocator))
}
}
}
linkage := ctx.api.get_cursor_linkage(cursor)
@@ -1406,6 +1420,7 @@ visit_cursor :: proc "c"(cursor, parent: CXCursor, client_data: rawptr) -> i32 {
append(&ctx.result.functions, Function{
name=fmt.aprintf("%s", name, allocator=ctx.allocator),
params=params[:],
param_names=param_names[:],
result=result_type,
variadic=variadic,
link_name=fmt.aprintf("%s", link_name, allocator=ctx.allocator),
+1 -1
View File
@@ -1110,7 +1110,7 @@ parse_params :: proc(parser: ^Parser) -> ([]ast.Param, bool) {
names: [dynamic]token.Token
names.allocator = parser.module.allocator
for {
if current(parser).kind != .Identifier {
if current(parser).kind != .Identifier && current(parser).kind != .Underscore {
source.add(parser.diagnostics, current(parser).span, "expected parameter name")
break
}
+20 -5
View File
@@ -12,6 +12,7 @@ package translatec
// the output is an honest record of the whole header.
import "../cimport"
import "../lexer"
import "core:fmt"
import "core:mem"
import "core:strings"
@@ -102,7 +103,8 @@ render_type :: proc(b: ^strings.Builder, result: ^cimport.Result, id: cimport.Ty
fmt.sbprintf(b, "[%d]", item.count)
render_type(b, result, item.child, record_names)
case .Function:
render_c_func(b, result, item.params, item.child, item.variadic, record_names)
// Function-pointer types carry no parameter names, so every slot renders `_`.
render_c_func(b, result, item.params, nil, item.child, item.variadic, record_names)
case .Record:
if int(item.record) >= 0 && int(item.record) < len(record_names) {
strings.write_string(b, record_names[item.record])
@@ -112,12 +114,14 @@ render_type :: proc(b: ^strings.Builder, result: ^cimport.Result, id: cimport.Ty
}
}
// render_c_func writes `c_func(arg0 T0, ...) R`. Params are named arg0.. because
// the parser requires parameter names; names do not affect type identity.
// render_c_func writes `c_func(name T0, ...) R`, using the real C parameter name
// when `names` provides one and `_` (the sink) otherwise. Names do not affect type
// identity; the parser requires a name slot, so unnamed params use `_`.
render_c_func :: proc(
b: ^strings.Builder,
result: ^cimport.Result,
params: []cimport.Type_Id,
names: []string,
ret: cimport.Type_Id,
variadic: bool,
record_names: []string,
@@ -127,7 +131,8 @@ render_c_func :: proc(
if index > 0 {
strings.write_string(b, ", ")
}
fmt.sbprintf(b, "arg%d ", index)
name := names[index] if index < len(names) else ""
fmt.sbprintf(b, "%s ", safe_param_name(name))
render_type(b, result, param, record_names)
}
if variadic {
@@ -267,7 +272,7 @@ emit_functions :: proc(b: ^strings.Builder, result: ^cimport.Result, record_name
continue
}
fmt.sbprintf(b, "%s :: ", function.name)
render_c_func(b, result, function.params, function.result, function.variadic, record_names)
render_c_func(b, result, function.params, function.param_names, function.result, function.variadic, record_names)
strings.write_byte(b, '\n')
wrote = true
}
@@ -311,6 +316,16 @@ render_macro_value :: proc(b: ^strings.Builder, value: cimport.Macro_Value) {
}
}
// safe_param_name returns the C parameter name when it is a usable brolang
// identifier, else `_` (the sink): empty names (unnamed C params) and names that
// collide with a brolang keyword (e.g. legal C `int f(int and)`) both become `_`.
safe_param_name :: proc(name: string) -> string {
if len(name) > 0 && lexer.keyword_kind(name) == .Identifier {
return name
}
return "_"
}
macro_scalar_kind :: proc(result: ^cimport.Result, id: cimport.Type_Id) -> bool {
if int(id) < 0 || int(id) >= len(result.types) {
return false