From 6fdc6a812c87aa387585f605b29a83dac2f10cad Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 25 Mar 2026 22:55:31 +0300 Subject: [PATCH] fmt: fix v fmt can create uncompilable code (fixes #26035) --- vlib/v/ast/str.v | 35 ++++++++++++++++++- .../testdata/vmodules/c_typedef_alias_keep.vv | 11 ++++++ vlib/v/fmt/testdata/vmodules/cmod/cmod.c.v | 6 ++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 vlib/v/fmt/testdata/vmodules/c_typedef_alias_keep.vv create mode 100644 vlib/v/fmt/testdata/vmodules/cmod/cmod.c.v diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index bc979d25c..54bf0f364 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -294,6 +294,39 @@ struct StringifyModReplacement { weight int } +fn is_qualified_name_boundary(c u8) bool { + return !(c.is_letter() || c.is_digit() || c == `_` || c == `.`) +} + +fn replace_qualified_name_based_on_alias(input string, mod string, alias string) string { + if mod.len == 0 || !input.contains(mod) { + return input + } + mut start := 0 + mut changed := false + mut sb := strings.new_builder(input.len) + for { + idx := input.index_after(mod, start) or { break } + end := idx + mod.len + before_ok := idx == 0 || is_qualified_name_boundary(input[idx - 1]) + after_ok := end == input.len || input[end] == `.` || is_qualified_name_boundary(input[end]) + if before_ok && after_ok { + sb.write_string(input[start..idx]) + sb.write_string(alias) + start = end + changed = true + continue + } + sb.write_string(input[start..end]) + start = end + } + if !changed { + return input + } + sb.write_string(input[start..]) + return sb.str() +} + fn shorten_full_name_based_on_aliases(input string, m2a map[string]string) string { if m2a.len == 0 || -1 == input.index_u8(`.`) { // a simple typename, like `string` or `[]bool`; no module aliasings apply, @@ -341,7 +374,7 @@ fn shorten_full_name_based_on_aliases(input string, m2a map[string]string) strin // r.mod: `v.token` | r.alias: `xyz` | res: `v.token.Abc` -> `xyz.Abc` // r.mod: `v.ast` | r.alias: `ast` | res: `v.ast.AliasTypeDecl` -> `ast.AliasTypeDecl` // r.mod: `v.ast` | r.alias: `ast` | res: `[]v.ast.InterfaceEmbedding` -> `[]ast.InterfaceEmbedding` - res = res.replace(r.mod, r.alias) + res = replace_qualified_name_based_on_alias(res, r.mod, r.alias) } return res } diff --git a/vlib/v/fmt/testdata/vmodules/c_typedef_alias_keep.vv b/vlib/v/fmt/testdata/vmodules/c_typedef_alias_keep.vv new file mode 100644 index 000000000..dbf18fb39 --- /dev/null +++ b/vlib/v/fmt/testdata/vmodules/c_typedef_alias_keep.vv @@ -0,0 +1,11 @@ +module main + +import cmod as cm + +fn returns_struct() !&C.cmod_struct_t { + return unsafe { &C.cmod_struct_t(nil) } +} + +fn main() { + println(returns_struct()!) +} diff --git a/vlib/v/fmt/testdata/vmodules/cmod/cmod.c.v b/vlib/v/fmt/testdata/vmodules/cmod/cmod.c.v new file mode 100644 index 000000000..66c64e424 --- /dev/null +++ b/vlib/v/fmt/testdata/vmodules/cmod/cmod.c.v @@ -0,0 +1,6 @@ +module cmod + +@[typedef] +pub struct C.cmod_struct {} + +pub type C.cmod_struct_t = C.cmod_struct -- 2.39.5