dte test coverage


Directory: ./
File: src/syntax/syntax.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 95 137 69.3%
Functions: 19 22 86.4%
Branches: 49 72 68.1%

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