From 5dcfbf927b272bfcc843633363cc2b63638caad7 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 25 Mar 2026 16:42:23 +0300 Subject: [PATCH] reflection: rework attrs handling (fixes #21537) --- vlib/v/gen/c/reflection.v | 22 ++++++++++++-------- vlib/v/reflection/reflection.v | 13 ++++++------ vlib/v/tests/reflection_attr_quotes_test.v | 7 ++++++- vlib/v/tests/reflection_sym_test.v | 24 ++++++++++++++++++++-- vlib/v/tests/reflection_test.v | 10 ++++++++- 5 files changed, 57 insertions(+), 19 deletions(-) diff --git a/vlib/v/gen/c/reflection.v b/vlib/v/gen/c/reflection.v index bafeaea45..e2b646d00 100644 --- a/vlib/v/gen/c/reflection.v +++ b/vlib/v/gen/c/reflection.v @@ -65,6 +65,7 @@ fn (mut g Gen) gen_reflection_fn(node ast.Fn) string { v_name := node.name.all_after_last('.') arg_str += '.mod_name=_S("${node.mod}"),' arg_str += '.name=_S("${v_name}"),' + arg_str += '.attrs=${g.gen_attrs_array(node.attrs)},' arg_str += '.args=${g.gen_functionarg_array(cprefix + 'FunctionArg', node)},' arg_str += '.file_idx=${g.reflection_string(util.cescaped_path(node.file))},' arg_str += '.line_start=${node.pos.line_nr},' @@ -87,23 +88,26 @@ fn (mut g Gen) gen_reflection_sym(tsym ast.TypeSymbol) string { return '(${cprefix}TypeSymbol){.name=_S("${name}"),.mod=_S("${tsym.mod}"),.idx=${tsym.idx},.parent_idx=${tsym.parent_idx},.language=${cprefix}VLanguage__${tsym.language},.kind=${cprefix}VKind__${kind_name},.info=${info},.methods=${methods}}' } -// gen_attrs_array generates C code for []Attr +// gen_attrs_array generates C code for []VAttribute. @[inline] fn (g &Gen) gen_attrs_array(attrs []ast.Attr) string { if attrs.len == 0 { - return g.gen_empty_array('string') + return g.gen_empty_array('VAttribute') } - mut out := 'builtin__new_array_from_c_array(${attrs.len},${attrs.len},sizeof(string),' - out += '_MOV((string[${attrs.len}]){' - out += attrs.map(if it.has_arg { - '_S("${it.name}=${escape_quotes(it.arg)}")' - } else { - '_S("${it.name}")' - }).join(',') + mut out := 'builtin__new_array_from_c_array(${attrs.len},${attrs.len},sizeof(VAttribute),' + out += '_MOV((VAttribute[${attrs.len}]){' + out += attrs.map(attr_to_vattribute_init(it)).join(',') out += '}))' return out } +@[inline] +fn attr_to_vattribute_init(attr ast.Attr) string { + name := cescape_nonascii(util.smart_quote(attr.name, false)) + arg := cescape_nonascii(util.smart_quote(attr.arg, false)) + return '(VAttribute){.name=_S("${name}"),.has_arg=${attr.has_arg},.arg=_S("${arg}"),.kind=${int(attr.kind)}}' +} + // gen_fields_array generates C code for []StructField @[inline] fn (g &Gen) gen_fields_array(fields []ast.StructField) string { diff --git a/vlib/v/reflection/reflection.v b/vlib/v/reflection/reflection.v index 3090d1628..4e02e68da 100644 --- a/vlib/v/reflection/reflection.v +++ b/vlib/v/reflection/reflection.v @@ -147,17 +147,17 @@ pub: pub struct StructField { pub: - name string // field name - typ VType // type - attrs []string // field attrs - is_pub bool // is pub? - is_mut bool // is mut? + name string // field name + typ VType // type + attrs []VAttribute // field attrs + is_pub bool // is pub? + is_mut bool // is mut? } pub struct Struct { pub: parent_idx int // parent type - attrs []string // struct attrs + attrs []VAttribute // struct attrs fields []StructField // fields } @@ -225,6 +225,7 @@ pub struct Function { pub: mod_name string // module name name string // function/method name + attrs []VAttribute // function/method attrs args []FunctionArg // function/method args file_idx int // source file name line_start int // decl start line diff --git a/vlib/v/tests/reflection_attr_quotes_test.v b/vlib/v/tests/reflection_attr_quotes_test.v index 0ecce7f9c..69c81b130 100644 --- a/vlib/v/tests/reflection_attr_quotes_test.v +++ b/vlib/v/tests/reflection_attr_quotes_test.v @@ -9,7 +9,12 @@ fn test_main() { a := MyParams{} t := r.type_of(a) if t.sym.info is r.Struct { - assert t.sym.info.fields[0].attrs[2] == 'xdoc=String to use as simulated FPGA version in Version responses. Must be in the form "a.bb.cccc"' + assert t.sym.info.fields[0].attrs[2] == VAttribute{ + name: 'xdoc' + has_arg: true + arg: 'String to use as simulated FPGA version in Version responses. Must be in the form "a.bb.cccc"' + kind: .string + } } else { assert false } diff --git a/vlib/v/tests/reflection_sym_test.v b/vlib/v/tests/reflection_sym_test.v index d517fb22a..10e630a96 100644 --- a/vlib/v/tests/reflection_sym_test.v +++ b/vlib/v/tests/reflection_sym_test.v @@ -99,7 +99,12 @@ fn test_struct_sym() { assert var.sym.kind == .struct assert var.sym.mod == 'main' assert (var.sym.info as reflection.Struct).attrs.len == 1 - assert (var.sym.info as reflection.Struct).attrs == ['test_struct'] + assert (var.sym.info as reflection.Struct).attrs == [ + VAttribute{ + name: 'test_struct' + kind: .plain + }, + ] field := (var.sym.info as reflection.Struct).fields[0] field_typ := field.typ @@ -109,12 +114,27 @@ fn test_struct_sym() { assert (field_sym.sym.info as reflection.Map).key_type.idx() == typeof[int]().idx assert (field_sym.sym.info as reflection.Map).value_type.idx() == typeof[string]().idx assert field.attrs.len == 1 + assert field.attrs == [ + VAttribute{ + name: 'test' + kind: .plain + }, + ] field2 := (var.sym.info as reflection.Struct).fields[1] field2_typ := (var.sym.info as reflection.Struct).fields[1].typ assert field2_typ.has_flag(.option) assert field2.name == 'n' assert field2.attrs.len == 2 - assert field2.attrs == ['test2', 'test3'] + assert field2.attrs == [ + VAttribute{ + name: 'test2' + kind: .plain + }, + VAttribute{ + name: 'test3' + kind: .plain + }, + ] assert field2.is_pub == false } diff --git a/vlib/v/tests/reflection_test.v b/vlib/v/tests/reflection_test.v index 5ce9be461..afb12d5c7 100644 --- a/vlib/v/tests/reflection_test.v +++ b/vlib/v/tests/reflection_test.v @@ -29,7 +29,15 @@ fn test_module_existing() { } fn test_func_attribute() { - assert reflection.get_funcs().filter(it.name == 'test3')[0].is_variadic == false + func := reflection.get_funcs().filter(it.name == 'test3')[0] + assert func.is_variadic == false + assert func.attrs == [ + VAttribute{ + name: 'noreturn' + kind: .plain + }, + ] + assert reflection.get_funcs().filter(it.name == 'test2')[0].attrs.len == 0 } fn test_func_name() { -- 2.39.5