From e104a5f571107323ade881a064326088d5e3d174 Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Sun, 14 Sep 2025 11:59:56 +0800 Subject: [PATCH] checker: disallow duplicate export names (fix #25301) (#25302) --- vlib/v/ast/table.v | 5 ++-- vlib/v/checker/checker.v | 28 +++++++++++++++++++ vlib/v/checker/fn.v | 16 +++++++++++ .../tests/redfine_global_const_fn_names.out | 28 +++++++++++++++++++ .../tests/redfine_global_const_fn_names.vv | 26 +++++++++++++++++ 5 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 vlib/v/checker/tests/redfine_global_const_fn_names.out create mode 100644 vlib/v/checker/tests/redfine_global_const_fn_names.vv diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index c2e9ace25..9cef439a4 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -99,8 +99,9 @@ pub mut: anon_union_names map[string]int // anon union name -> union sym idx anon_union_counter int comptime_is_true map[string]ComptTimeCondResult // The evaluate cond results for different generic types combination, such as `comptime_is_true['T=int,X=string|main.v|pos ...'] = {true, '!DEFINED(WINDOWS)'}` - new_int bool // use 64bit/32bit platform dependent `int` - new_int_fmt_fix bool // vfmt will fix `int` to `i32` + new_int bool // use 64bit/32bit platform dependent `int` + new_int_fmt_fix bool // vfmt will fix `int` to `i32` + export_names map[string]string // @[export] names } pub struct ComptTimeCondResult { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index dff88767f..9dd559fb3 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1937,6 +1937,13 @@ fn (mut c Checker) const_decl(mut node ast.ConstDecl) { c.warn('const () groups will be an error after 2025-01-01 (`v fmt -w source.v` will fix that for you)', node.pos) } + + mut is_export := false + mut export_name := '' + if export_attr := node.attrs.find_first('export') { + is_export = true + export_name = export_attr.arg + } for mut field in node.fields { if reserved_type_names_chk.matches(util.no_cur_mod(field.name, c.mod)) { c.error('invalid use of reserved type `${field.name}` as a const name', field.pos) @@ -1949,6 +1956,21 @@ fn (mut c Checker) const_decl(mut node ast.ConstDecl) { } c.error('duplicate const `${field.name}`', name_pos) } + if is_export { + check_name := if export_name.len == 0 { field.name } else { export_name } + if !check_name.is_identifier() { + c.error('export name `${check_name}` should be a valid identifier', node.pos) + } + if check_name in c.table.export_names.values() { + name_pos := token.Pos{ + ...field.pos + len: util.no_cur_mod(field.name, c.mod).len + } + c.error('duplicate export name `${check_name}`', name_pos) + } else { + c.table.export_names[field.name] = check_name + } + } if field.expr is ast.CallExpr { sym := c.table.sym(c.check_expr_option_or_result_call(field.expr, c.expr(mut field.expr))) if sym.kind == .multi_return { @@ -2522,6 +2544,12 @@ fn (mut c Checker) global_decl(mut node ast.GlobalDecl) { if '${c.mod}.${field.name}' in c.const_names { c.error('duplicate global and const `${field.name}`', field.pos) } + if field.name in c.table.export_names.values() { + c.error('duplicate export name `${field.name}`', field.pos) + } else { + // add global to exports for duplicate check + c.table.export_names[field.name] = field.name + } sym := c.table.sym(field.typ) if sym.kind == .placeholder { c.error('unknown type `${sym.name}`', field.typ_pos) diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index a07e73b0e..fb6495573 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -115,6 +115,22 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { if attr := node.attrs.find_first(attr_name) { if attr.arg == '' { c.error('missing argument for @[${attr_name}] attribute', attr.pos) + } else if attr_name == 'export' { + // export fn name + fn_name := attr.arg + if !fn_name.is_identifier() { + c.error('export name `${fn_name}` should be a valid identifier', + node.pos) + } + if fn_name in c.table.export_names.values() { + c.error('duplicate export name `${fn_name}`', node.pos) + } else { + mut node_name := node.name + if node.is_method { + node_name = c.table.type_to_str(node.receiver.typ) + '.' + node_name + } + c.table.export_names[node_name] = fn_name + } } } } diff --git a/vlib/v/checker/tests/redfine_global_const_fn_names.out b/vlib/v/checker/tests/redfine_global_const_fn_names.out new file mode 100644 index 000000000..9cc12054d --- /dev/null +++ b/vlib/v/checker/tests/redfine_global_const_fn_names.out @@ -0,0 +1,28 @@ +vlib/v/checker/tests/redfine_global_const_fn_names.vv:10:7: error: duplicate export name `abc` + 8 | + 9 | @[export: 'abc'] + 10 | const abc2 = 3 + | ~~~~ + 11 | + 12 | @[export: 'fn1'] +vlib/v/checker/tests/redfine_global_const_fn_names.vv:4:10: error: duplicate export name `abc` + 2 | module main + 3 | + 4 | __global abc = 1 + | ~~~ + 5 | + 6 | @[export: 'abc'] +vlib/v/checker/tests/redfine_global_const_fn_names.vv:17:1: error: duplicate export name `fn1` + 15 | + 16 | @[export: 'fn1'] + 17 | fn fn_abc2() { + | ~~~~~~~~~~~~ + 18 | } + 19 | +vlib/v/checker/tests/redfine_global_const_fn_names.vv:21:1: error: export name `abc.dd` should be a valid identifier + 19 | + 20 | @[export: 'abc.dd'] + 21 | fn fn_abc3() { + | ~~~~~~~~~~~~ + 22 | } + 23 | diff --git a/vlib/v/checker/tests/redfine_global_const_fn_names.vv b/vlib/v/checker/tests/redfine_global_const_fn_names.vv new file mode 100644 index 000000000..fdc3b13b7 --- /dev/null +++ b/vlib/v/checker/tests/redfine_global_const_fn_names.vv @@ -0,0 +1,26 @@ +@[has_globals] +module main + +__global abc = 1 + +@[export: 'abc'] +const abc1 = 2 + +@[export: 'abc'] +const abc2 = 3 + +@[export: 'fn1'] +fn fn_abc1() { +} + +@[export: 'fn1'] +fn fn_abc2() { +} + +@[export: 'abc.dd'] +fn fn_abc3() { +} + +@[export: xyz] +fn fn_xyz() { +} -- 2.39.5