dte test coverage


Directory: ./
File: src/syntax/merge.c
Date: 2025-09-07 23:01:39
Exec Total Coverage
Lines: 60 62 96.8%
Functions: 5 5 100.0%
Branches: 21 26 80.8%

Line Branch Exec Source
1 #include <errno.h>
2 #include <stdbool.h>
3 #include <string.h>
4 #include "merge.h"
5 #include "util/debug.h"
6 #include "util/hashmap.h"
7 #include "util/numtostr.h"
8 #include "util/string-view.h"
9 #include "util/xmalloc.h"
10 #include "util/xstring.h"
11
12 enum {
13 FIXBUF_SIZE = 512
14 };
15
16 4401 static const char *fix_name(char *buf, StringView prefix, const char *name)
17 {
18 4401 size_t plen = prefix.length;
19 4401 BUG_ON(plen >= FIXBUF_SIZE);
20 4401 size_t avail = FIXBUF_SIZE - plen;
21 4401 char *end = memccpy(xmempcpy(buf, prefix.data, plen), name, '\0', avail);
22
1/2
✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→8) taken 4401 times.
4401 FATAL_ERROR_ON(!end, ENOBUFS);
23 4401 return buf;
24 }
25
26 3635 static void fix_action(const Syntax *syn, Action *a, StringView prefix, char *buf)
27 {
28
2/2
✓ Branch 0 (2→3) taken 2821 times.
✓ Branch 1 (2→6) taken 814 times.
3635 if (a->destination) {
29 2821 const char *name = fix_name(buf, prefix, a->destination->name);
30 2821 a->destination = find_state(syn, name);
31 }
32 3635 }
33
34 1405 static void fix_conditions (
35 const Syntax *syn,
36 State *s,
37 const SyntaxMerge *m,
38 StringView prefix,
39 char *buf
40 ) {
41
2/2
✓ Branch 0 (11→3) taken 2230 times.
✓ Branch 1 (11→12) taken 1405 times.
3635 for (size_t i = 0, n = s->conds.count; i < n; i++) {
42 2230 Condition *c = s->conds.ptrs[i];
43 2230 fix_action(syn, &c->a, prefix, buf);
44
4/4
✓ Branch 0 (4→5) taken 411 times.
✓ Branch 1 (4→7) taken 1819 times.
✓ Branch 2 (5→6) taken 324 times.
✓ Branch 3 (5→7) taken 87 times.
2230 if (!c->a.destination && cond_type_has_destination(c->type)) {
45 324 c->a.destination = m->return_state;
46 }
47
1/4
✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→10) taken 2230 times.
✗ Branch 2 (8→9) not taken.
✗ Branch 3 (8→10) not taken.
2230 if (m->delim && c->type == COND_HEREDOCEND) {
48 c->u.heredocend = string_view(m->delim, m->delim_len);
49 }
50 }
51
52 1405 fix_action(syn, &s->default_action, prefix, buf);
53
2/2
✓ Branch 0 (13→14) taken 403 times.
✓ Branch 1 (13→15) taken 1002 times.
1405 if (!s->default_action.destination) {
54 403 s->default_action.destination = m->return_state;
55 }
56 1405 }
57
58 // Generate a prefix for merged state names, to avoid clashes
59 175 static StringView make_prefix_str(char *buf)
60 {
61 175 static unsigned int counter;
62 175 size_t n = 0;
63 175 buf[n++] = 'm';
64 175 n += buf_uint_to_str(counter++, buf + n);
65 175 buf[n++] = '-';
66 175 return string_view(buf, n);
67 }
68
69 // Merge a sub-syntax into another syntax, copying or updating
70 // pointers and strings as appropriate.
71 // NOTE: string_lists is owned by Syntax, so there's no need to
72 // copy it. Freeing Condition does not free any string lists.
73 175 State *merge_syntax(Syntax *syn, SyntaxMerge *merge, const StyleMap *styles)
74 {
75 175 const HashMap *subsyn_states = &merge->subsyn->states;
76 175 HashMap *states = &syn->states;
77 175 char prefix_buf[64];
78 175 StringView prefix = make_prefix_str(prefix_buf);
79 175 char buf[FIXBUF_SIZE];
80
81
2/2
✓ Branch 0 (20→4) taken 1405 times.
✓ Branch 1 (20→21) taken 175 times.
1580 for (HashMapIter it = hashmap_iter(subsyn_states); hashmap_next(&it); ) {
82 1405 State *s = xmemdup(it.entry->value, sizeof(State));
83 1405 s->name = xmemjoin(prefix.data, prefix.length, s->name, strlen(s->name) + 1);
84 1405 hashmap_insert(states, s->name, s);
85
86
2/2
✓ Branch 0 (7→8) taken 1385 times.
✓ Branch 1 (7→16) taken 20 times.
1405 if (s->conds.count > 0) {
87 // Deep copy conds PointerArray
88 1385 BUG_ON(s->conds.alloc < s->conds.count);
89 1385 void **ptrs = xmallocarray(s->conds.alloc, sizeof(*ptrs));
90
2/2
✓ Branch 0 (14→12) taken 2230 times.
✓ Branch 1 (14→15) taken 1385 times.
3615 for (size_t i = 0, n = s->conds.count; i < n; i++) {
91 2230 ptrs[i] = xmemdup(s->conds.ptrs[i], sizeof(Condition));
92 }
93 1385 s->conds.ptrs = ptrs;
94 } else {
95 20 BUG_ON(s->conds.alloc != 0);
96 }
97
98 // Mark unvisited, so that return-only states get visited
99 1405 s->visited = false;
100
101 // Don't complain about unvisited, copied states
102 1405 s->copied = true;
103 }
104
105 // Fix conditions and update styles for newly merged states
106
2/2
✓ Branch 0 (31→22) taken 1405 times.
✓ Branch 1 (31→32) taken 175 times.
1755 for (HashMapIter it = hashmap_iter(subsyn_states); hashmap_next(&it); ) {
107 1405 const State *subsyn_state = it.entry->value;
108 1405 BUG_ON(!subsyn_state);
109 1405 const char *new_name = fix_name(buf, prefix, subsyn_state->name);
110 1405 State *new_state = hashmap_xget(states, new_name);
111 1405 fix_conditions(syn, new_state, merge, prefix, buf);
112
1/2
✗ Branch 0 (27→28) not taken.
✓ Branch 1 (27→30) taken 1405 times.
1405 if (merge->delim) {
113 update_state_styles(syn, new_state, styles);
114 }
115 }
116
117 175 const char *name = fix_name(buf, prefix, merge->subsyn->start_state->name);
118 175 State *start_state = hashmap_xget(states, name);
119 175 merge->subsyn->used = true;
120 175 return start_state;
121 }
122