dte test coverage


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