dte test coverage


Directory: ./
File: src/ctags.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 70 70 100.0%
Functions: 5 5 100.0%
Branches: 51 60 85.0%

Line Branch Exec Source
1 #include <stdlib.h>
2 #include "ctags.h"
3 #include "util/ascii.h"
4 #include "util/debug.h"
5 #include "util/str-util.h"
6 #include "util/strtonum.h"
7 #include "util/xmalloc.h"
8 #include "util/xstring.h"
9
10 19 static size_t parse_ex_pattern(const char *buf, size_t size, char **escaped)
11 {
12 19 BUG_ON(size == 0);
13 19 BUG_ON(buf[0] != '/' && buf[0] != '?');
14
15 // The search pattern is not a real regular expression; special characters
16 // need to be escaped
17 19 char *pattern = xmalloc(size * 2);
18 19 char open_delim = buf[0];
19
2/2
✓ Branch 0 (20→8) taken 890 times.
✓ Branch 1 (20→21) taken 1 times.
891 for (size_t i = 1, j = 0; i < size; i++) {
20
2/2
✓ Branch 0 (8→9) taken 889 times.
✓ Branch 1 (8→21) taken 1 times.
890 if (unlikely(buf[i] == '\0')) {
21 break;
22 }
23
3/4
✓ Branch 0 (9→10) taken 3 times.
✓ Branch 1 (9→14) taken 886 times.
✓ Branch 2 (10→11) taken 3 times.
✗ Branch 3 (10→14) not taken.
889 if (buf[i] == '\\' && i + 1 < size) {
24 3 i++;
25
2/2
✓ Branch 0 (11→12) taken 1 times.
✓ Branch 1 (11→13) taken 2 times.
3 if (buf[i] == '\\') {
26 1 pattern[j++] = '\\';
27 }
28 3 pattern[j++] = buf[i];
29 3 continue;
30 }
31
2/2
✓ Branch 0 (14→15) taken 17 times.
✓ Branch 1 (14→16) taken 869 times.
886 if (buf[i] == open_delim) {
32 17 pattern[j] = '\0';
33 17 *escaped = pattern;
34 17 return i + 1;
35 }
36 869 char c = buf[i];
37
2/2
✓ Branch 0 (16→17) taken 28 times.
✓ Branch 1 (16→18) taken 841 times.
869 if (c == '*' || c == '[' || c == ']') {
38 28 pattern[j++] = '\\';
39 }
40 869 pattern[j++] = buf[i];
41 }
42
43 2 free(pattern);
44 2 return 0;
45 }
46
47 21 static size_t parse_ex_cmd(Tag *tag, const char *buf, size_t size)
48 {
49
1/2
✓ Branch 0 (2→3) taken 21 times.
✗ Branch 1 (2→11) not taken.
21 if (unlikely(size == 0)) {
50 return 0;
51 }
52
53 21 size_t n;
54
2/2
✓ Branch 0 (3→4) taken 19 times.
✓ Branch 1 (3→5) taken 2 times.
21 if (buf[0] == '/' || buf[0] == '?') {
55 19 n = parse_ex_pattern(buf, size, &tag->pattern);
56 } else {
57 2 n = buf_parse_ulong(buf, size, &tag->lineno);
58 }
59
60
2/2
✓ Branch 0 (6→7) taken 19 times.
✓ Branch 1 (6→11) taken 2 times.
21 if (n == 0) {
61 return 0;
62 }
63
64
4/4
✓ Branch 0 (7→8) taken 17 times.
✓ Branch 1 (7→11) taken 2 times.
✓ Branch 2 (9→10) taken 16 times.
✓ Branch 3 (9→11) taken 1 times.
19 if (n + 1 < size && mem_equal(buf + n, ";\"", 2)) {
65 16 n += 2;
66 }
67
68 return n;
69 }
70
71 21 bool parse_ctags_line(Tag *tag, const char *line, size_t line_len)
72 {
73 21 size_t pos = 0;
74 21 *tag = (Tag){.name = get_delim(line, &pos, line_len, '\t')};
75
2/4
✓ Branch 0 (3→4) taken 21 times.
✗ Branch 1 (3→25) not taken.
✓ Branch 2 (4→5) taken 21 times.
✗ Branch 3 (4→25) not taken.
21 if (tag->name.length == 0 || pos >= line_len) {
76 return false;
77 }
78
79 21 tag->filename = get_delim(line, &pos, line_len, '\t');
80
2/4
✓ Branch 0 (6→7) taken 21 times.
✗ Branch 1 (6→25) not taken.
✓ Branch 2 (7→8) taken 21 times.
✗ Branch 3 (7→25) not taken.
21 if (tag->filename.length == 0 || pos >= line_len) {
81 return false;
82 }
83
84 21 size_t len = parse_ex_cmd(tag, line + pos, line_len - pos);
85
2/2
✓ Branch 0 (9→10) taken 2 times.
✓ Branch 1 (9→12) taken 19 times.
21 if (len == 0) {
86 2 BUG_ON(tag->pattern);
87 return false;
88 }
89
90 19 pos += len;
91
2/2
✓ Branch 0 (12→13) taken 18 times.
✓ Branch 1 (12→25) taken 1 times.
19 if (pos >= line_len) {
92 return true;
93 }
94
95 /*
96 * Extension fields (key:[value]):
97 *
98 * file: visibility limited to this file
99 * struct:NAME tag is member of struct NAME
100 * union:NAME tag is member of union NAME
101 * typeref:struct:NAME::MEMBER_TYPE MEMBER_TYPE is type of the tag
102 */
103
2/2
✓ Branch 0 (13→14) taken 1 times.
✓ Branch 1 (13→24) taken 17 times.
18 if (line[pos++] != '\t') {
104 // free `pattern` allocated by parse_ex_cmd()
105 1 free_tag(tag);
106 1 tag->pattern = NULL;
107 1 return false;
108 }
109
110
2/2
✓ Branch 0 (24→16) taken 38 times.
✓ Branch 1 (24→25) taken 17 times.
55 while (pos < line_len) {
111 38 StringView field = get_delim(line, &pos, line_len, '\t');
112
3/4
✓ Branch 0 (17→18) taken 17 times.
✓ Branch 1 (17→20) taken 21 times.
✓ Branch 2 (18→19) taken 17 times.
✗ Branch 3 (18→20) not taken.
38 if (field.length == 1 && ascii_isalpha(field.data[0])) {
113 17 tag->kind = field.data[0];
114
2/2
✓ Branch 0 (21→22) taken 7 times.
✓ Branch 1 (21→23) taken 14 times.
21 } else if (strview_equal_cstring(&field, "file:")) {
115 7 tag->local = true;
116 }
117 // TODO: struct/union/typeref
118 }
119
120 return true;
121 }
122
123 18 bool next_tag (
124 const char *buf,
125 size_t buf_len,
126 size_t *posp, // in-out param
127 const StringView *prefix,
128 bool exact,
129 Tag *tag // out param
130 ) {
131 18 const char *p = prefix->data;
132 18 size_t plen = prefix->length;
133
2/2
✓ Branch 0 (14→3) taken 55 times.
✓ Branch 1 (14→15) taken 4 times.
59 for (size_t pos = *posp; pos < buf_len; ) {
134 55 StringView line = buf_slice_next_line(buf, &pos, buf_len);
135 55 if (
136
1/2
✓ Branch 0 (4→5) taken 55 times.
✗ Branch 1 (4→13) not taken.
55 line.length > 0 // Line is non-empty
137
2/2
✓ Branch 0 (5→6) taken 37 times.
✓ Branch 1 (5→13) taken 18 times.
55 && line.data[0] != '!' // and not a comment
138
2/2
✓ Branch 0 (7→8) taken 15 times.
✓ Branch 1 (7→13) taken 22 times.
37 && strview_has_strn_prefix(&line, p, plen) // and starts with `prefix`
139
4/4
✓ Branch 0 (8→9) taken 2 times.
✓ Branch 1 (8→10) taken 13 times.
✓ Branch 2 (9→10) taken 1 times.
✓ Branch 3 (9→13) taken 1 times.
15 && (!exact || line.data[plen] == '\t') // or matches `prefix` exactly, if applicable
140
1/2
✓ Branch 0 (11→12) taken 14 times.
✗ Branch 1 (11→13) not taken.
14 && parse_ctags_line(tag, line.data, line.length) // and is a valid tags(5) entry
141 ) {
142 // Advance the position; `tag` param has been filled by parse_ctags_line()
143 14 *posp = pos;
144 14 return true;
145 }
146 }
147
148 // No matching tags remaining
149 4 return false;
150 }
151
152 // NOTE: tag itself is not freed
153 22 void free_tag(Tag *tag)
154 {
155 22 free(tag->pattern);
156 22 }
157