dte test coverage


Directory: ./
File: src/ctags.c
Date: 2024-12-21 16:03:22
Exec Total Coverage
Lines: 71 72 98.6%
Functions: 5 5 100.0%
Branches: 50 60 83.3%

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 18 static size_t parse_ex_pattern(const char *buf, size_t size, char **escaped)
11 {
12 18 BUG_ON(size == 0);
13 18 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 18 char *pattern = xmalloc(size * 2);
18 18 char open_delim = buf[0];
19
2/2
✓ Branch 0 taken 846 times.
✓ Branch 1 taken 1 times.
847 for (size_t i = 1, j = 0; i < size; i++) {
20
2/2
✓ Branch 0 taken 845 times.
✓ Branch 1 taken 1 times.
846 if (unlikely(buf[i] == '\0')) {
21 break;
22 }
23
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 842 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
845 if (buf[i] == '\\' && i + 1 < size) {
24 3 i++;
25
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 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 taken 16 times.
✓ Branch 1 taken 826 times.
842 if (buf[i] == open_delim) {
32 16 pattern[j] = '\0';
33 16 *escaped = pattern;
34 16 return i + 1;
35 }
36 826 char c = buf[i];
37
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 801 times.
826 if (c == '*' || c == '[' || c == ']') {
38 25 pattern[j++] = '\\';
39 }
40 826 pattern[j++] = buf[i];
41 }
42
43 2 free(pattern);
44 2 return 0;
45 }
46
47 20 static size_t parse_ex_cmd(Tag *tag, const char *buf, size_t size)
48 {
49
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (unlikely(size == 0)) {
50 return 0;
51 }
52
53 20 size_t n;
54
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 2 times.
20 if (buf[0] == '/' || buf[0] == '?') {
55 18 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 taken 18 times.
✓ Branch 1 taken 2 times.
20 if (n == 0) {
61 return 0;
62 }
63
64
4/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 1 times.
18 if (n + 1 < size && mem_equal(buf + n, ";\"", 2)) {
65 15 n += 2;
66 }
67
68 return n;
69 }
70
71 20 bool parse_ctags_line(Tag *tag, const char *line, size_t line_len)
72 {
73 20 size_t pos = 0;
74 20 *tag = (Tag){.name = get_delim(line, &pos, line_len, '\t')};
75
2/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
20 if (tag->name.length == 0 || pos >= line_len) {
76 return false;
77 }
78
79 20 tag->filename = get_delim(line, &pos, line_len, '\t');
80
2/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
20 if (tag->filename.length == 0 || pos >= line_len) {
81 return false;
82 }
83
84 20 size_t len = parse_ex_cmd(tag, line + pos, line_len - pos);
85
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 18 times.
20 if (len == 0) {
86 2 BUG_ON(tag->pattern);
87 return false;
88 }
89
90 18 pos += len;
91
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1 times.
18 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 taken 1 times.
✓ Branch 1 taken 16 times.
17 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 taken 36 times.
✓ Branch 1 taken 16 times.
52 while (pos < line_len) {
111 36 StringView field = get_delim(line, &pos, line_len, '\t');
112
3/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
36 if (field.length == 1 && ascii_isalpha(field.data[0])) {
113 16 tag->kind = field.data[0];
114
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 13 times.
20 } 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 16 bool next_tag (
124 const char *buf,
125 size_t buf_len,
126 size_t *posp,
127 const StringView *prefix,
128 bool exact,
129 Tag *tag
130 ) {
131 16 const char *p = prefix->data;
132 16 size_t plen = prefix->length;
133
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 3 times.
57 for (size_t pos = *posp; pos < buf_len; ) {
134 54 StringView line = buf_slice_next_line(buf, &pos, buf_len);
135
3/4
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 36 times.
54 if (line.length == 0 || line.data[0] == '!') {
136 41 continue;
137 }
138
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 14 times.
36 if (!strview_has_strn_prefix(&line, p, plen)) {
139 22 continue;
140 }
141
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
14 if (exact && line.data[plen] != '\t') {
142 1 continue;
143 }
144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (!parse_ctags_line(tag, line.data, line.length)) {
145 continue;
146 }
147 13 *posp = pos;
148 13 return true;
149 }
150 3 return false;
151 }
152
153 // NOTE: tag itself is not freed
154 21 void free_tag(Tag *tag)
155 {
156 21 free(tag->pattern);
157 21 }
158