v / vlib / v2 / ast / flat_prepend_to_fn_body_test.v
110 lines · 98 sloc · 3.75 KB · f3c5760b8838272e4789c38a1be9e03f9ceac351
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module ast
5
6// Tests for `FlatBuilder.prepend_to_fn_body`. The helper is the seed primitive
7// for the remaining prepend-style post_pass mutations
8// (`inject_main_runtime_const_init_calls`, `inject_live_reload`): it re-emits
9// a stmt_fn_decl with extras prepended to its body, returning the new
10// FlatNodeId so callers can rewire the surrounding file/stmt list.
11//
12// We compare via `subtree_signature(fn_id)` rather than the whole-file
13// `signature()` because the file root's `extra=intern(mod)` slot would leak
14// intern order between the reference (which builds extras before file mod
15// gets re-interned) and subject (which interns extras AFTER file mod) paths.
16
17fn make_fn_decl_with_body(name string, body []Stmt) Stmt {
18 return Stmt(FnDecl{
19 name: name
20 stmts: body
21 })
22}
23
24fn make_const_stmt(name string, value string) Stmt {
25 return Stmt(ConstDecl{
26 fields: [
27 FieldInit{
28 name: name
29 value: Expr(BasicLiteral{
30 kind: .number
31 value: value
32 })
33 },
34 ]
35 })
36}
37
38// Reference: emit a FnDecl whose body is already `[extras + old]`.
39fn build_reference_fn_with_prepended() (FlatAst, FlatNodeId) {
40 mut b := new_flat_builder()
41 fn_id := b.emit_stmt(make_fn_decl_with_body('foo', [
42 make_const_stmt('A', '1'),
43 make_const_stmt('B', '2'),
44 make_const_stmt('C', '3'),
45 ]))
46 return b.flat, fn_id
47}
48
49// Subject: emit a FnDecl whose body is `[old]`, emit extras separately,
50// then prepend via the primitive under test.
51fn build_subject_fn_with_prepended() (FlatAst, FlatNodeId) {
52 mut b := new_flat_builder()
53 base_fn_id := b.emit_stmt(make_fn_decl_with_body('foo', [
54 make_const_stmt('C', '3'),
55 ]))
56 a_id := b.emit_stmt(make_const_stmt('A', '1'))
57 b_id := b.emit_stmt(make_const_stmt('B', '2'))
58 new_fn_id := b.prepend_to_fn_body(base_fn_id, [a_id, b_id])
59 return b.flat, new_fn_id
60}
61
62fn test_prepend_to_fn_body_signature_matches_reference() {
63 ref_flat, ref_fn_id := build_reference_fn_with_prepended()
64 sub_flat, sub_fn_id := build_subject_fn_with_prepended()
65 ref_sig := ref_flat.subtree_signature(ref_fn_id)
66 sub_sig := sub_flat.subtree_signature(sub_fn_id)
67 assert ref_sig == sub_sig
68}
69
70fn test_prepend_to_fn_body_zero_extras_returns_existing_id() {
71 mut b := new_flat_builder()
72 original_id := b.emit_stmt(make_fn_decl_with_body('foo', [
73 make_const_stmt('C', '3'),
74 ]))
75 returned_id := b.prepend_to_fn_body(original_id, [])
76 assert returned_id == original_id
77}
78
79fn test_prepend_to_fn_body_invalid_id_returns_invalid() {
80 mut b := new_flat_builder()
81 a_id := b.emit_stmt(make_const_stmt('A', '1'))
82 returned_id := b.prepend_to_fn_body(FlatNodeId(-1), [a_id])
83 assert returned_id == invalid_flat_node_id
84}
85
86fn test_prepend_to_fn_body_non_fn_decl_returns_invalid() {
87 // Pass a non-FnDecl node id (a ConstDecl) — primitive must refuse.
88 mut b := new_flat_builder()
89 const_id := b.emit_stmt(make_const_stmt('C', '3'))
90 extra_id := b.emit_stmt(make_const_stmt('A', '1'))
91 returned_id := b.prepend_to_fn_body(const_id, [extra_id])
92 assert returned_id == invalid_flat_node_id
93}
94
95fn test_prepend_to_fn_body_empty_original_body() {
96 // FnDecl with no original body stmts; prepending should still produce a
97 // new FnDecl whose body is exactly the extras.
98 ref_flat, ref_fn_id := fn () (FlatAst, FlatNodeId) {
99 mut b := new_flat_builder()
100 fn_id := b.emit_stmt(make_fn_decl_with_body('foo', [make_const_stmt('A', '1')]))
101 return b.flat, fn_id
102 }()
103
104 mut sb := new_flat_builder()
105 base_fn_id := sb.emit_stmt(make_fn_decl_with_body('foo', []))
106 a_id := sb.emit_stmt(make_const_stmt('A', '1'))
107 new_fn_id := sb.prepend_to_fn_body(base_fn_id, [a_id])
108
109 assert ref_flat.subtree_signature(ref_fn_id) == sb.flat.subtree_signature(new_fn_id)
110}
111