dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 80.7% 109 / 0 / 135
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 5563 State *find_state(const Syntax *syn, const char *name)
14 {
15 5563 return hashmap_get(&syn->states, name);
16 }
17
18 576 Syntax *find_any_syntax(const HashMap *syntaxes, const char *name)
19 {
20 576 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 6125 static void visit(State *s)
28 {
29
2/2
✓ Branch 2 → 3 taken 2330 times.
✓ Branch 2 → 10 taken 3795 times.
6125 if (s->visited) {
30 return;
31 }
32 2330 s->visited = true;
33
2/2
✓ Branch 7 → 4 taken 4135 times.
✓ Branch 7 → 8 taken 2330 times.
6465 for (size_t i = 0, n = s->conds.count; i < n; i++) {
34 4135 const Condition *cond = s->conds.ptrs[i];
35
2/2
✓ Branch 4 → 5 taken 3809 times.
✓ Branch 4 → 6 taken 326 times.
4135 if (cond->a.destination) {
36 3809 visit(cond->a.destination);
37 }
38 }
39
2/2
✓ Branch 8 → 9 taken 2169 times.
✓ Branch 8 → 10 taken 161 times.
2330 if (s->default_action.destination) {
40 2169 visit(s->default_action.destination);
41 }
42 }
43
44 4138 static void free_condition(Condition *cond)
45 {
46 4138 free(cond);
47 4138 }
48
49 static void free_heredoc_state(HeredocState *s)
50 {
51 free(s);
52 }
53
54 2339 static void free_state(State *s)
55 {
56 2339 ptr_array_free_cb(&s->conds, FREE_FUNC(free_condition));
57 2339 ptr_array_free_cb(&s->heredoc.states, FREE_FUNC(free_heredoc_state));
58 2339 free(s);
59 2339 }
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 154 static void free_syntax_contents(Syntax *syn)
68 {
69 154 hashmap_free(&syn->states, FREE_FUNC(free_state));
70 154 hashmap_free(&syn->string_lists, FREE_FUNC(free_string_list));
71 154 hashmap_free(&syn->default_styles, NULL);
72 154 }
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 147 static void free_syntax_cb(Syntax *syn)
82 {
83 147 free_syntax_contents(syn);
84 147 free(syn);
85 147 }
86
87 11 void free_syntaxes(HashMap *syntaxes)
88 {
89 11 hashmap_free(syntaxes, FREE_FUNC(free_syntax_cb));
90 11 }
91
92 152 bool finalize_syntax(HashMap *syntaxes, Syntax *syn, ErrorBuffer *ebuf)
93 {
94
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 151 times.
152 if (syn->states.count == 0) {
95 1 return error_msg(ebuf, "Empty syntax");
96 }
97
98
2/2
✓ Branch 9 → 5 taken 2337 times.
✓ Branch 9 → 10 taken 150 times.
2487 for (HashMapIter it = hashmap_iter(&syn->states); hashmap_next(&it); ) {
99 2337 const State *s = it.entry->value;
100
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 8 taken 2336 times.
2337 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 149 times.
252 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 139 times.
✓ Branch 17 → 18 taken 1 time.
✓ Branch 17 → 19 taken 9 times.
149 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 147 times.
148 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 147 visit(syn->start_state);
124
2/2
✓ Branch 29 → 24 taken 2331 times.
✓ Branch 29 → 30 taken 147 times.
2478 for (HashMapIter it = hashmap_iter(&syn->states); hashmap_next(&it); ) {
125 2331 const State *s = it.entry->value;
126
3/4
✓ Branch 24 → 25 taken 1 time.
✓ Branch 24 → 27 taken 2330 times.
✓ Branch 25 → 26 taken 1 time.
✗ Branch 25 → 27 not taken.
2331 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 147 times.
396 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 147 hashmap_insert(syntaxes, syn->name, syn);
139 147 return true;
140 }
141
142 240 Syntax *find_syntax(const HashMap *syntaxes, const char *name)
143 {
144 240 Syntax *syn = find_any_syntax(syntaxes, name);
145
3/4
✓ Branch 3 → 4 taken 125 times.
✓ Branch 3 → 6 taken 115 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 125 times.
240 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 4277 static const char *get_effective_emit_name(const Action *a)
157 {
158
2/2
✓ Branch 2 → 3 taken 3115 times.
✓ Branch 2 → 4 taken 1162 times.
4277 return a->emit_name ? a->emit_name : a->destination->emit_name;
159 }
160
161 4277 static void update_action_style(const Syntax *syn, Action *a, const StyleMap *styles)
162 {
163 4277 const char *name = get_effective_emit_name(a);
164 4277 char full[256];
165 4277 xsnprintf(full, sizeof full, "%s.%s", syn->name, name);
166 4277 a->emit_style = find_style(styles, full);
167
2/2
✓ Branch 4 → 5 taken 666 times.
✓ Branch 4 → 10 taken 3611 times.
4277 if (a->emit_style) {
168 3951 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 1492 void update_state_styles(const Syntax *syn, State *s, const StyleMap *styles)
181 {
182
2/2
✓ Branch 5 → 3 taken 2785 times.
✓ Branch 5 → 6 taken 1492 times.
4277 for (size_t i = 0, n = s->conds.count; i < n; i++) {
183 2785 Condition *c = s->conds.ptrs[i];
184 2785 update_action_style(syn, &c->a, styles);
185 }
186 1492 update_action_style(syn, &s->default_action, styles);
187 1492 }
188
189 136 void update_syntax_styles(Syntax *syn, const StyleMap *styles)
190 {
191
2/2
✓ Branch 2 → 3 taken 57 times.
✓ Branch 2 → 8 taken 79 times.
136 if (is_subsyntax(syn)) {
192 // No point in updating styles of a sub-syntax
193 return;
194 }
195
2/2
✓ Branch 6 → 4 taken 1492 times.
✓ Branch 6 → 7 taken 57 times.
1549 for (HashMapIter it = hashmap_iter(&syn->states); hashmap_next(&it); ) {
196 1492 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 136 times.
✓ Branch 5 → 6 taken 1 time.
137 for (HashMapIter it = hashmap_iter(syntaxes); hashmap_next(&it); ) {
203 136 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 4666 times.
✓ Branch 10 → 11 taken 64 times.
4730 for (HashMapIter it = hashmap_iter(syntaxes); hashmap_next(&it); ) {
210 4666 Syntax *s = it.entry->value;
211
6/6
✓ Branch 3 → 4 taken 2026 times.
✓ Branch 3 → 8 taken 2640 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.
4666 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 const char *prefix
223 ) {
224 size_t prefix_len = strlen(prefix);
225 HashSet set;
226 hashset_init(&set, 16, false);
227
228 // Insert all `Action::emit_name` strings beginning with `prefix` into
229 // a HashSet (to avoid duplicates)
230 for (HashMapIter it = hashmap_iter(&syntax->states); hashmap_next(&it); ) {
231 const State *s = it.entry->value;
232 const char *emit = get_effective_emit_name(&s->default_action);
233 if (str_has_strn_prefix(emit, prefix, prefix_len)) {
234 hashset_insert(&set, emit, strlen(emit));
235 }
236 for (size_t i = 0, n = s->conds.count; i < n; i++) {
237 const Condition *cond = s->conds.ptrs[i];
238 emit = get_effective_emit_name(&cond->a);
239 if (str_has_strn_prefix(emit, prefix, prefix_len)) {
240 hashset_insert(&set, emit, strlen(emit));
241 }
242 }
243 }
244
245 const char *ft = syntax->name;
246 size_t ftlen = strlen(ft);
247
248 // Append the collected strings to the PointerArray
249 for (HashSetIter iter = hashset_iter(&set); hashset_next(&iter); ) {
250 const HashSetEntry *h = iter.entry;
251 char *str = xmemjoin3(ft, ftlen, STRN("."), h->str, h->str_len + 1);
252 ptr_array_append(a, str);
253 }
254
255 hashset_free(&set);
256 }
257