diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 85840788d7d92b..a0a6815f82f545 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -883,27 +883,54 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } } } - if fn_name.ends_with('from_string') { - enum_name := fn_name.all_before('__static__') - full_enum_name := if !enum_name.contains('.') { - c.mod + '.' + enum_name - } else { - enum_name + } + // Enum.from_string, `mod.Enum.from_string('item')`, `Enum.from_string('item')` + if !found && fn_name.ends_with('__static__from_string') { + enum_name := fn_name.all_before('__static__') + mut full_enum_name := if !enum_name.contains('.') { + c.mod + '.' + enum_name + } else { + enum_name + } + mut idx := c.table.type_idxs[full_enum_name] + if idx > 0 { + // is from another mod. + if enum_name.contains('.') { + if !c.check_type_and_visibility(full_enum_name, idx, .enum_, node.pos) { + return ast.void_type + } } - idx := c.table.type_idxs[full_enum_name] - ret_typ := ast.Type(idx).set_flag(.option) - if node.args.len != 1 { - c.error('expected 1 argument, but got ${node.args.len}', node.pos) - } else { - node.args[0].typ = c.expr(mut node.args[0].expr) - if node.args[0].typ != ast.string_type { - styp := c.table.type_to_str(node.args[0].typ) - c.error('expected `string` argument, but got `${styp}`', node.pos) + } else if !enum_name.contains('.') { + // find from another mods. + for import_sym in c.file.imports { + full_enum_name = '${import_sym.mod}.${enum_name}' + idx = c.table.type_idxs[full_enum_name] + if idx < 1 { + continue } + if !c.check_type_and_visibility(full_enum_name, idx, .enum_, node.pos) { + return ast.void_type + } + break + } + } + if idx == 0 { + c.error('unknown enum `${enum_name}`', node.pos) + return ast.void_type + } + + ret_typ := ast.Type(idx).set_flag(.option) + if node.args.len != 1 { + c.error('expected 1 argument, but got ${node.args.len}', node.pos) + } else { + node.args[0].typ = c.expr(mut node.args[0].expr) + if node.args[0].typ != ast.string_type { + styp := c.table.type_to_str(node.args[0].typ) + c.error('expected `string` argument, but got `${styp}`', node.pos) } - node.return_type = ret_typ - return ret_typ } + node.return_type = ret_typ + return ret_typ } mut is_native_builtin := false if !found && c.pref.backend == .native { @@ -1622,6 +1649,24 @@ fn (mut c Checker) cast_to_fixed_array_ret(typ ast.Type, sym ast.TypeSymbol) ast return typ } +// checks if a type from another module is is expected and visible(`is_pub`) +fn (mut c Checker) check_type_and_visibility(name string, type_idx int, expected_kind &ast.Kind, pos &token.Pos) bool { + mut sym := c.table.sym_by_idx(type_idx) + if sym.kind == .alias { + parent_type := (sym.info as ast.Alias).parent_type + sym = c.table.sym(parent_type) + } + if sym.kind != expected_kind { + c.error('expected ${expected_kind}, but `${name}` is ${sym.kind}', pos) + return false + } + if !sym.is_pub { + c.error('module `${sym.mod}` type `${sym.name}` is private', pos) + return false + } + return true +} + fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { left_type := c.expr(mut node.left) if left_type == ast.void_type { diff --git a/vlib/v/checker/tests/modules/enum_from_string_in_different_mods.out b/vlib/v/checker/tests/modules/enum_from_string_in_different_mods.out new file mode 100644 index 00000000000000..32a15977a8b58d --- /dev/null +++ b/vlib/v/checker/tests/modules/enum_from_string_in_different_mods.out @@ -0,0 +1,40 @@ +vlib/v/checker/tests/modules/enum_from_string_in_different_mods/src/main.v:3:8: warning: module 'mod' is imported but never used + 1 | module main + 2 | + 3 | import mod { MyEnum, MyStruct } + | ~~~ + 4 | + 5 | fn main() { +vlib/v/checker/tests/modules/enum_from_string_in_different_mods/src/main.v:3:14: error: module `mod` type `MyEnum` is private + 1 | module main + 2 | + 3 | import mod { MyEnum, MyStruct } + | ~~~~~~ + 4 | + 5 | fn main() { +vlib/v/checker/tests/modules/enum_from_string_in_different_mods/src/main.v:6:7: error: module `mod` type `mod.MyEnum` is private + 4 | + 5 | fn main() { + 6 | _ := MyEnum.from_string('item1') + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 7 | _ := MyStruct.from_string('item1') + 8 | } +vlib/v/checker/tests/modules/enum_from_string_in_different_mods/src/main.v:6:4: error: assignment mismatch: 1 variable(s) but `MyEnum.from_string()` returns 0 value(s) + 4 | + 5 | fn main() { + 6 | _ := MyEnum.from_string('item1') + | ~~ + 7 | _ := MyStruct.from_string('item1') + 8 | } +vlib/v/checker/tests/modules/enum_from_string_in_different_mods/src/main.v:7:7: error: expected enum, but `mod.MyStruct` is struct + 5 | fn main() { + 6 | _ := MyEnum.from_string('item1') + 7 | _ := MyStruct.from_string('item1') + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 8 | } +vlib/v/checker/tests/modules/enum_from_string_in_different_mods/src/main.v:7:4: error: assignment mismatch: 1 variable(s) but `MyStruct.from_string()` returns 0 value(s) + 5 | fn main() { + 6 | _ := MyEnum.from_string('item1') + 7 | _ := MyStruct.from_string('item1') + | ~~ + 8 | } diff --git a/vlib/v/checker/tests/modules/enum_from_string_in_different_mods/src/main.v b/vlib/v/checker/tests/modules/enum_from_string_in_different_mods/src/main.v new file mode 100644 index 00000000000000..c174a0b32bd159 --- /dev/null +++ b/vlib/v/checker/tests/modules/enum_from_string_in_different_mods/src/main.v @@ -0,0 +1,8 @@ +module main + +import mod { MyEnum, MyStruct } + +fn main() { + _ := MyEnum.from_string('item1') + _ := MyStruct.from_string('item1') +} diff --git a/vlib/v/checker/tests/modules/enum_from_string_in_different_mods/src/mod.v b/vlib/v/checker/tests/modules/enum_from_string_in_different_mods/src/mod.v new file mode 100644 index 00000000000000..f4b913ac225fef --- /dev/null +++ b/vlib/v/checker/tests/modules/enum_from_string_in_different_mods/src/mod.v @@ -0,0 +1,8 @@ +module mod + +enum MyEnum { + item1 + item2 +} + +pub struct MyStruct {} diff --git a/vlib/v/checker/tests/modules/enum_from_string_in_different_mods/v.mod b/vlib/v/checker/tests/modules/enum_from_string_in_different_mods/v.mod new file mode 100644 index 00000000000000..d00491fd7e5bb6 --- /dev/null +++ b/vlib/v/checker/tests/modules/enum_from_string_in_different_mods/v.mod @@ -0,0 +1 @@ +1 diff --git a/vlib/v/gen/c/auto_str_methods.v b/vlib/v/gen/c/auto_str_methods.v index de09f3c9c486d0..538894cfdc1661 100644 --- a/vlib/v/gen/c/auto_str_methods.v +++ b/vlib/v/gen/c/auto_str_methods.v @@ -1150,12 +1150,22 @@ fn should_use_indent_func(kind ast.Kind) bool { fn (mut g Gen) gen_enum_static_from_string(fn_name string) { enum_name := fn_name.all_before('__static__') - mod_enum_name := if !enum_name.contains('.') { + mut mod_enum_name := if !enum_name.contains('.') { g.cur_mod.name + '.' + enum_name } else { enum_name } - idx := g.table.type_idxs[mod_enum_name] + mut idx := g.table.type_idxs[mod_enum_name] + if idx == 0 && (enum_name.contains('.') || enum_name[0].is_capital()) { + // no cur mod, find from another mods. + for import_sym in g.file.imports { + mod_enum_name = '${import_sym.mod}.${enum_name}' + idx = g.table.type_idxs[mod_enum_name] + if idx > 0 { + break + } + } + } enum_typ := ast.Type(idx) enum_styp := g.typ(enum_typ) option_enum_typ := ast.Type(idx).set_flag(.option) @@ -1164,9 +1174,10 @@ fn (mut g Gen) gen_enum_static_from_string(fn_name string) { enum_field_vals := g.table.get_enum_field_vals(mod_enum_name) mut fn_builder := strings.new_builder(512) - g.definitions.writeln('static ${option_enum_styp} ${fn_name}(string name); // auto') + fn_name_no_dots := util.no_dots(fn_name) + g.definitions.writeln('static ${option_enum_styp} ${fn_name_no_dots}(string name); // auto') - fn_builder.writeln('static ${option_enum_styp} ${fn_name}(string name) {') + fn_builder.writeln('static ${option_enum_styp} ${fn_name_no_dots}(string name) {') fn_builder.writeln('\t${option_enum_styp} t1;') fn_builder.writeln('\tbool exists = false;') fn_builder.writeln('\tint inx = 0;') diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 2f396bf48ea49e..f38597fb1ca297 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1803,7 +1803,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { g.gen_enum_static_from_string(node.name) g.str_fn_names << node.name } - g.write('${node.name}(') + g.write('${util.no_dots(node.name)}(') g.call_args(node) g.write(')') } else { diff --git a/vlib/v/tests/modules/enum_from_string_in_different_mods/src/main_test.v b/vlib/v/tests/modules/enum_from_string_in_different_mods/src/main_test.v new file mode 100644 index 00000000000000..a9766bfc54cb79 --- /dev/null +++ b/vlib/v/tests/modules/enum_from_string_in_different_mods/src/main_test.v @@ -0,0 +1,10 @@ +module main + +import mod { MyEnum } + +fn test_main() { + item1 := MyEnum.from_string('item1')? + assert item1 == MyEnum.item1 + item2 := mod.MyEnum.from_string('item2')? + assert item2 == MyEnum.item2 +} diff --git a/vlib/v/tests/modules/enum_from_string_in_different_mods/src/mod/mod.v b/vlib/v/tests/modules/enum_from_string_in_different_mods/src/mod/mod.v new file mode 100644 index 00000000000000..41609555e44baa --- /dev/null +++ b/vlib/v/tests/modules/enum_from_string_in_different_mods/src/mod/mod.v @@ -0,0 +1,6 @@ +module mod + +pub enum MyEnum { + item1 + item2 +} diff --git a/vlib/v/tests/modules/enum_from_string_in_different_mods/v.mod b/vlib/v/tests/modules/enum_from_string_in_different_mods/v.mod new file mode 100644 index 00000000000000..d00491fd7e5bb6 --- /dev/null +++ b/vlib/v/tests/modules/enum_from_string_in_different_mods/v.mod @@ -0,0 +1 @@ +1