From edd6f3d380805cf6aea9d23fd01b2ac8f3996c1b Mon Sep 17 00:00:00 2001 From: Maokaman1 Date: Tue, 31 Mar 2026 16:03:21 +0300 Subject: [PATCH] cgen: stop treating every .m as an Objective-C import (#26794) cgen was using a plain `contains('.m')` check when deciding whether a hash directive should be deferred until after V type definitions. That worked for real Objective-C includes, but it also caught unrelated cases like a `#insert` under a `@VMODROOT` path that happened to include a `.m` directory, or a `#define` value containing `.m`. When that happened, the directive was moved out of the normal include section and never emitted. Tighten the check so only `#include` and `#preinclude` directives whose target file actually ends in `.m` get the Objective-C treatment, and add regression tests for both the `#insert` and `#define` cases. --- vlib/v/gen/c/cgen.v | 24 +++++++++++++++---- ...hash_stmt_dot_m_false_positive.c.must_have | 2 ++ .../hash_stmt_dot_m_false_positive.m.h | 1 + .../hash_stmt_dot_m_false_positive.vv | 6 +++++ 4 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 vlib/v/gen/c/testdata/hash_stmt_dot_m_false_positive.c.must_have create mode 100644 vlib/v/gen/c/testdata/hash_stmt_dot_m_false_positive.m.h create mode 100644 vlib/v/gen/c/testdata/hash_stmt_dot_m_false_positive.vv diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 9144a479d..a7b0c4c88 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -6179,10 +6179,11 @@ fn (mut g Gen) hash_stmt(node ast.HashStmt) { } else { &ast.HashStmtNode(&node) } + is_objc_target := is_objc_hash_target(node.kind, node.main) match node.kind { 'include', 'insert', 'define' { - if node.main.contains('.m') { + if is_objc_target { // Objective C code import, include it after V types, so that e.g. `string` is // available there if the_node !in g.definition_nodes { @@ -6195,7 +6196,7 @@ fn (mut g Gen) hash_stmt(node ast.HashStmt) { } } 'preinclude' { - if node.main.contains('.m') { + if is_objc_target { // Objective C code import, include it after V types, so that e.g. `string` is // available there if the_node !in g.definition_nodes { @@ -6216,6 +6217,18 @@ fn (mut g Gen) hash_stmt(node ast.HashStmt) { } } +fn is_objc_hash_target(kind string, raw_target string) bool { + if kind !in ['include', 'preinclude'] { + return false + } + mut target := raw_target.trim_space() + if target.len >= 2 && ((target.starts_with('"') && target.ends_with('"')) + || (target.starts_with('<') && target.ends_with('>'))) { + target = target[1..target.len - 1] + } + return os.file_name(target).ends_with('.m') +} + fn (mut g Gen) gen_hash_stmts(mut sb strings.Builder, node &ast.HashStmtNode, section string) { match node { ast.IfExpr { @@ -6272,12 +6285,13 @@ fn (mut g Gen) gen_hash_stmts(mut sb strings.Builder, node &ast.HashStmtNode, se // #inlude,#define,#insert => `includes` section // #postinclude => `postincludes` section // '*.m' in #include or #preinclude => `definitions` section + is_objc_target := is_objc_hash_target(node.kind, node.main) need_gen_stmt := match section { 'preincludes' { - if node.kind == 'preinclude' && !node.main.contains('.m') { true } else { false } + if node.kind == 'preinclude' && !is_objc_target { true } else { false } } 'includes' { - if node.kind in ['include', 'define', 'insert'] && !node.main.contains('.m') { + if node.kind in ['include', 'define', 'insert'] && !is_objc_target { true } else { false @@ -6286,7 +6300,7 @@ fn (mut g Gen) gen_hash_stmts(mut sb strings.Builder, node &ast.HashStmtNode, se 'definitions' { // Objective C code import, include it after V types, so that e.g. `string` is // available there - if node.kind in ['include', 'preinclude'] && node.main.contains('.m') { + if node.kind in ['include', 'preinclude'] && is_objc_target { true } else { false diff --git a/vlib/v/gen/c/testdata/hash_stmt_dot_m_false_positive.c.must_have b/vlib/v/gen/c/testdata/hash_stmt_dot_m_false_positive.c.must_have new file mode 100644 index 000000000..b39e5b523 --- /dev/null +++ b/vlib/v/gen/c/testdata/hash_stmt_dot_m_false_positive.c.must_have @@ -0,0 +1,2 @@ +#define DOT_M_INSERT_OK 1 +#define DOT_M_DEFINE_OK "tmp/project.m/value.h" diff --git a/vlib/v/gen/c/testdata/hash_stmt_dot_m_false_positive.m.h b/vlib/v/gen/c/testdata/hash_stmt_dot_m_false_positive.m.h new file mode 100644 index 000000000..eca379046 --- /dev/null +++ b/vlib/v/gen/c/testdata/hash_stmt_dot_m_false_positive.m.h @@ -0,0 +1 @@ +#define DOT_M_INSERT_OK 1 diff --git a/vlib/v/gen/c/testdata/hash_stmt_dot_m_false_positive.vv b/vlib/v/gen/c/testdata/hash_stmt_dot_m_false_positive.vv new file mode 100644 index 000000000..bfb425997 --- /dev/null +++ b/vlib/v/gen/c/testdata/hash_stmt_dot_m_false_positive.vv @@ -0,0 +1,6 @@ +// This reproduces a false positive in the old `.contains('.m')` check: +// neither the inserted header nor the define value is an Objective-C `.m` import. +#insert "@DIR/hash_stmt_dot_m_false_positive.m.h" +#define DOT_M_DEFINE_OK "tmp/project.m/value.h" + +fn main() {} -- 2.39.5