dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 82.0% 109 / 0 / 133
Functions: 90.9% 20 / 0 / 22
Branches: 82.9% 58 / 0 / 70

src/syntax/syntax.c
Line Branch Exec Source
1 #include <stdlib.h>
2 #include <string.h>
3 #include "syntax.h"
4 #include "util/xmalloc.h"
5 #include "util/xsnprintf.h"
6
7 207 StringList *find_string_list(const Syntax *syn, const char *name)
8 {
9 207 return hashmap_get(&syn->string_lists, name);
10 }
11
12 5615 State *find_state(const Syntax *syn, const char *name)
13 {
14 5615 return hashmap_get(&syn->states, name);
15 }
16
17 585 Syntax *find_any_syntax(const HashMap *syntaxes, const char *name)
18 {
19 585 return hashmap_get(syntaxes, name);
20 }
21
22 // Each State is visited at most once (when reachable) and recursion
23 // is bounded by the longest chain of `Action::destination` pointers
24 // (typically not more than 20 or so)
25 // NOLINTNEXTLINE(misc-no-recursion)
26 6222 static void visit(State *s)
27 {
28
2/2
✓ Branch 2 → 3 taken 2379 times.
✓ Branch 2 → 10 taken 3843 times.
6222 if (s->visited) {
29 return;
30 }
31 2379 s->visited = true;
32
2/2
✓ Branch 7 → 4 taken 4206 times.
✓ Branch 7 → 8 taken 2379 times.
6585 for (size_t i = 0, n = s->conds.count; i < n; i++) {
33 4206 const Condition *cond = s->conds.ptrs[i];
34
2/2
✓ Branch 4 → 5 taken 3871 times.
✓ Branch 4 → 6 taken 335 times.
4206 if (cond->a.destination) {
35 3871 visit(cond->a.destination);
36 }
37 }
38
2/2
✓ Branch 8 → 9 taken 2202 times.
✓ Branch 8 → 10 taken 177 times.
2379 if (s->default_action.destination) {
39 2202 visit(s->default_action.destination);
40 }
41 }
42
43 4209 static void free_condition(Condition *cond)
44 {
45 4209 free(cond);
46 4209 }
47
48 ✗ static void free_heredoc_state(HeredocState *s)
49 {
50 ✗ free(s);
51 ✗ }
52
53 2388 static void free_state(State *s)
54 {
55 2388 ptr_array_free_cb(&s->conds, FREE_FUNC(free_condition));
56 2388 ptr_array_free_cb(&s->heredoc.states, FREE_FUNC(free_heredoc_state));
57 2388 free(s);
58 2388 }
59
60 103 static void free_string_list(StringList *list)
61 {
62 103 hashset_free(&list->strings);
63 103 free(list);
64 103 }
65
66 156 static void free_syntax_contents(Syntax *syn)
67 {
68 156 hashmap_free(&syn->states, FREE_FUNC(free_state));
69 156 hashmap_free(&syn->string_lists, FREE_FUNC(free_string_list));
70 156 hashmap_free(&syn->default_styles, NULL);
71 156 }
72
73 7 void free_syntax(Syntax *syn)
74 {
75 7 free_syntax_contents(syn);
76 7 free(syn->name);
77 7 free(syn);
78 7 }
79
80 149 static void free_syntax_cb(Syntax *syn)
81 {
82 149 free_syntax_contents(syn);
83 149 free(syn);
84 149 }
85
86 11 void free_syntaxes(HashMap *syntaxes)
87 {
88 11 hashmap_free(syntaxes, FREE_FUNC(free_syntax_cb));
89 11 }
90
91 154 bool finalize_syntax(HashMap *syntaxes, Syntax *syn, ErrorBuffer *ebuf)
92 {
93
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 153 times.
154 if (syn->states.count == 0) {
94 1 return error_msg(ebuf, "Empty syntax");
95 }
96
97
2/2
✓ Branch 9 → 5 taken 2386 times.
✓ Branch 9 → 10 taken 152 times.
2538 for (HashMapIter it = hashmap_iter(&syn->states); hashmap_next(&it); ) {
98 2386 const State *s = it.entry->value;
99
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 8 taken 2385 times.
2386 if (!s->defined) {
100 // This state has been referenced but not defined
101 1 return error_msg(ebuf, "No such state '%s'", it.entry->key);
102 }
103 }
104
2/2
✓ Branch 15 → 11 taken 103 times.
✓ Branch 15 → 16 taken 151 times.
254 for (HashMapIter it = hashmap_iter(&syn->string_lists); hashmap_next(&it); ) {
105 103 const StringList *list = it.entry->value;
106
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 14 taken 102 times.
103 if (!list->defined) {
107 1 return error_msg(ebuf, "No such list '%s'", it.entry->key);
108 }
109 }
110
111
4/4
✓ Branch 16 → 17 taken 10 times.
✓ Branch 16 → 19 taken 141 times.
✓ Branch 17 → 18 taken 1 time.
✓ Branch 17 → 19 taken 9 times.
151 if (syn->heredoc && !is_subsyntax(syn)) {
112 1 return error_msg(ebuf, "heredocend can be used only in subsyntaxes");
113 }
114
115
2/2
✓ Branch 20 → 21 taken 1 time.
✓ Branch 20 → 22 taken 149 times.
150 if (find_any_syntax(syntaxes, syn->name)) {
116 1 return error_msg(ebuf, "Syntax '%s' already exists", syn->name);
117 }
118
119 // Unused states and lists cause warnings only (to make loading WIP
120 // syntax files less annoying)
121
122 149 visit(syn->start_state);
123
2/2
✓ Branch 29 → 24 taken 2380 times.
✓ Branch 29 → 30 taken 149 times.
2529 for (HashMapIter it = hashmap_iter(&syn->states); hashmap_next(&it); ) {
124 2380 const State *s = it.entry->value;
125
3/4
✓ Branch 24 → 25 taken 1 time.
✓ Branch 24 → 27 taken 2379 times.
✓ Branch 25 → 26 taken 1 time.
✗ Branch 25 → 27 not taken.
2380 if (!s->visited && !s->copied) {
126 1 error_msg(ebuf, "State '%s' is unreachable", it.entry->key);
127 }
128 }
129
130
2/2
✓ Branch 35 → 31 taken 102 times.
✓ Branch 35 → 36 taken 149 times.
400 for (HashMapIter it = hashmap_iter(&syn->string_lists); hashmap_next(&it); ) {
131 102 const StringList *list = it.entry->value;
132
2/2
✓ Branch 31 → 32 taken 1 time.
✓ Branch 31 → 34 taken 101 times.
102 if (!list->used) {
133 1 error_msg(ebuf, "List '%s' never used", it.entry->key);
134 }
135 }
136
137 149 hashmap_insert(syntaxes, syn->name, syn);
138 149 return true;
139 }
140
141 241 Syntax *find_syntax(const HashMap *syntaxes, const char *name)
142 {
143 241 Syntax *syn = find_any_syntax(syntaxes, name);
144
3/4
✓ Branch 3 → 4 taken 125 times.
✓ Branch 3 → 6 taken 116 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 125 times.
241 if (syn && is_subsyntax(syn)) {
145 ✗ return NULL;
146 }
147 return syn;
148 }
149
150 666 static const char *find_default_style(const Syntax *syn, const char *name)
151 {
152 666 return hashmap_get(&syn->default_styles, name);
153 }
154
155 4358 static const char *get_effective_emit_name(const Action *a)
156 {
157
2/2
✓ Branch 2 → 3 taken 3132 times.
✓ Branch 2 → 4 taken 1226 times.
4358 return a->emit_name ? a->emit_name : a->destination->emit_name;
158 }
159
160 4358 static void update_action_style(const Syntax *syn, Action *a, const StyleMap *styles)
161 {
162 4358 const char *name = get_effective_emit_name(a);
163 4358 char full[256];
164 4358 xsnprintf(full, sizeof full, "%s.%s", syn->name, name);
165 4358 a->emit_style = find_style(styles, full);
166
2/2
✓ Branch 4 → 5 taken 666 times.
✓ Branch 4 → 10 taken 3692 times.
4358 if (a->emit_style) {
167 4032 return;
168 }
169
170 666 const char *def = find_default_style(syn, name);
171
2/2
✓ Branch 6 → 7 taken 326 times.
✓ Branch 6 → 10 taken 340 times.
666 if (!def) {
172 return;
173 }
174
175 326 xsnprintf(full, sizeof full, "%s.%s", syn->name, def);
176 326 a->emit_style = find_style(styles, full);
177 }
178
179 1525 void update_state_styles(const Syntax *syn, State *s, const StyleMap *styles)
180 {
181
2/2
✓ Branch 5 → 3 taken 2833 times.
✓ Branch 5 → 6 taken 1525 times.
4358 for (size_t i = 0, n = s->conds.count; i < n; i++) {
182 2833 Condition *c = s->conds.ptrs[i];
183 2833 update_action_style(syn, &c->a, styles);
184 }
185 1525 update_action_style(syn, &s->default_action, styles);
186 1525 }
187
188 138 void update_syntax_styles(Syntax *syn, const StyleMap *styles)
189 {
190
2/2
✓ Branch 2 → 3 taken 57 times.
✓ Branch 2 → 8 taken 81 times.
138 if (is_subsyntax(syn)) {
191 // No point in updating styles of a sub-syntax
192 return;
193 }
194
2/2
✓ Branch 6 → 4 taken 1525 times.
✓ Branch 6 → 7 taken 57 times.
1582 for (HashMapIter it = hashmap_iter(&syn->states); hashmap_next(&it); ) {
195 1525 update_state_styles(syn, it.entry->value, styles);
196 }
197 }
198
199 1 void update_all_syntax_styles(const HashMap *syntaxes, const StyleMap *styles)
200 {
201
2/2
✓ Branch 5 → 3 taken 138 times.
✓ Branch 5 → 6 taken 1 time.
139 for (HashMapIter it = hashmap_iter(syntaxes); hashmap_next(&it); ) {
202 138 update_syntax_styles(it.entry->value, styles);
203 }
204 1 }
205
206 64 void find_unused_subsyntaxes(const HashMap *syntaxes, ErrorBuffer *ebuf)
207 {
208
2/2
✓ Branch 10 → 3 taken 4690 times.
✓ Branch 10 → 11 taken 64 times.
4754 for (HashMapIter it = hashmap_iter(syntaxes); hashmap_next(&it); ) {
209 4690 Syntax *s = it.entry->value;
210
6/6
✓ Branch 3 → 4 taken 2026 times.
✓ Branch 3 → 8 taken 2664 times.
✓ Branch 4 → 5 taken 2024 times.
✓ Branch 4 → 8 taken 2 times.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 8 taken 2023 times.
4690 if (!s->used && !s->warned_unused_subsyntax && is_subsyntax(s)) {
211 1 error_msg(ebuf, "Subsyntax '%s' is unused", s->name);
212 // Don't complain multiple times about the same unused subsyntaxes
213 1 s->warned_unused_subsyntax = true;
214 }
215 }
216 64 }
217
218 ✗ void collect_syntax_emit_names (
219 const Syntax *syntax,
220 PointerArray *a,
221 StringView prefix
222 ) {
223 ✗ HashSet set = hashset_new(16, false);
224
225 // Insert all `Action::emit_name` strings beginning with `prefix` into
226 // a HashSet (to avoid duplicates)
227 ✗ for (HashMapIter it = hashmap_iter(&syntax->states); hashmap_next(&it); ) {
228 ✗ const State *s = it.entry->value;
229 ✗ StringView emit = strview(get_effective_emit_name(&s->default_action));
230 ✗ if (strview_has_sv_prefix(emit, prefix)) {
231 ✗ hashset_insert(&set, emit);
232 }
233 ✗ for (size_t i = 0, n = s->conds.count; i < n; i++) {
234 ✗ const Condition *cond = s->conds.ptrs[i];
235 ✗ emit = strview(get_effective_emit_name(&cond->a));
236 ✗ if (strview_has_sv_prefix(emit, prefix)) {
237 ✗ hashset_insert(&set, emit);
238 }
239 }
240 }
241
242 ✗ const char *ft = syntax->name;
243 ✗ size_t ftlen = strlen(ft);
244
245 // Append the collected strings to the PointerArray
246 ✗ for (HashSetIter iter = hashset_iter(&set); hashset_next(&iter); ) {
247 ✗ const HashSetEntry *h = iter.entry;
248 ✗ char *str = xmemjoin3(ft, ftlen, STRN("."), h->str, h->str_len + 1);
249 ✗ ptr_array_append(a, str);
250 }
251
252 ✗ hashset_free(&set);
253 ✗ }
254