v / vlib / v2 / ast / flat_prepend_file_stmts_test.v
128 lines · 118 sloc · 3.98 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_file_stmts`. Sibling of `append_file_stmts`
7// (s150) — same shape, opposite order. Needed by `inject_live_reload`'s
8// file-level prepend where c_decls + global_decls go BEFORE the file's
9// existing stmts.
10
11fn make_minimal_file_with_stmts_prepend(extra_stmts []Stmt) File {
12 mut stmts := []Stmt{cap: 1 + extra_stmts.len}
13 for s in extra_stmts {
14 stmts << s
15 }
16 stmts << Stmt(ModuleStmt{
17 name: 'foo'
18 })
19 stmts << Stmt(ConstDecl{
20 fields: [
21 FieldInit{
22 name: 'TAIL'
23 value: Expr(BasicLiteral{
24 kind: .number
25 value: '0'
26 })
27 },
28 ]
29 })
30 return File{
31 name: 'inline_prepend.v'
32 mod: 'foo'
33 stmts: stmts
34 }
35}
36
37fn make_prepend_const_a() Stmt {
38 return Stmt(ConstDecl{
39 fields: [
40 FieldInit{
41 name: 'PA'
42 value: Expr(BasicLiteral{
43 kind: .number
44 value: '11'
45 })
46 },
47 ]
48 })
49}
50
51fn make_prepend_const_b() Stmt {
52 return Stmt(ConstDecl{
53 fields: [
54 FieldInit{
55 name: 'PB'
56 value: Expr(BasicLiteral{
57 kind: .number
58 value: '22'
59 })
60 },
61 ]
62 })
63}
64
65// Reference: construct the file with both prepended consts BEFORE the module
66// stmt at append time.
67//
68// Why this layout: prepend_file_stmts puts new stmts at the head of the
69// stmts list — even before the ModuleStmt — so the reference must mirror
70// that ordering at construction time to produce a bit-equal signature.
71//
72// Why subtree-of-stmts-list instead of full file signature: the file root's
73// `extra` slot stores `intern(mod)` as a raw index. When the reference path
74// emits the file's stmts in order, the prepended consts intern their strings
75// BEFORE 'foo' the module name → 'foo' lands at a later intern slot. The
76// subject path interns 'foo' first (via the bare file's ModuleStmt) → 'foo'
77// at slot 0. Comparing the stmts-list subtree sidesteps this leak: it covers
78// the actual prepended stmts ordering + content (what the primitive controls)
79// without including the file root's leaky `extra` field. The s155 multi-file
80// test uses the same subtree_signature workaround for the same reason.
81fn build_reference_stmts_list_signature() string {
82 mut b := new_flat_builder()
83 b.append_file(make_minimal_file_with_stmts_prepend([make_prepend_const_a(),
84 make_prepend_const_b()]))
85 stmts_list_id := b.flat.child_at(b.flat.files[0].file_id, 2)
86 return b.flat.subtree_signature(stmts_list_id)
87}
88
89// Subject: build the bare file, emit prepended stmts separately, then splice
90// them via prepend_file_stmts.
91fn build_subject_stmts_list_signature() string {
92 mut b := new_flat_builder()
93 b.append_file(make_minimal_file_with_stmts_prepend([]))
94 a_id := b.emit_stmt(make_prepend_const_a())
95 c_id := b.emit_stmt(make_prepend_const_b())
96 b.prepend_file_stmts(0, [a_id, c_id])
97 stmts_list_id := b.flat.child_at(b.flat.files[0].file_id, 2)
98 return b.flat.subtree_signature(stmts_list_id)
99}
100
101fn test_prepend_file_stmts_signature_matches_reference() {
102 ref_sig := build_reference_stmts_list_signature()
103 sub_sig := build_subject_stmts_list_signature()
104 assert ref_sig == sub_sig
105}
106
107fn test_prepend_file_stmts_zero_prepended_returns_existing_root() {
108 mut b := new_flat_builder()
109 original_id := b.append_file(make_minimal_file_with_stmts_prepend([]))
110 returned_id := b.prepend_file_stmts(0, [])
111 assert returned_id == original_id
112}
113
114fn test_prepend_file_stmts_invalid_idx_returns_invalid() {
115 mut b := new_flat_builder()
116 b.append_file(make_minimal_file_with_stmts_prepend([]))
117 returned_id := b.prepend_file_stmts(-1, [FlatNodeId(0)])
118 assert returned_id == invalid_flat_node_id
119}
120
121fn test_prepend_file_stmts_updates_files_table() {
122 mut b := new_flat_builder()
123 original_file_id := b.append_file(make_minimal_file_with_stmts_prepend([]))
124 x_id := b.emit_stmt(make_prepend_const_a())
125 new_file_id := b.prepend_file_stmts(0, [x_id])
126 assert new_file_id != original_file_id
127 assert b.flat.files[0].file_id == new_file_id
128}
129