rbtr-lang-c 2026.7.0.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rbtr_lang_c/__init__.py +1 -0
- rbtr_lang_c/c.scm +63 -0
- rbtr_lang_c/plugin.py +41 -0
- rbtr_lang_c/py.typed +0 -0
- rbtr_lang_c/tests/__init__.py +0 -0
- rbtr_lang_c/tests/__snapshots__/test_samples/test_edges_match_snapshot.json +6 -0
- rbtr_lang_c/tests/__snapshots__/test_samples/test_extraction_matches_snapshot.json +452 -0
- rbtr_lang_c/tests/cases_docstrings.py +88 -0
- rbtr_lang_c/tests/cases_extraction.py +137 -0
- rbtr_lang_c/tests/samples/c/c.c +44 -0
- rbtr_lang_c/tests/samples/c/greeter.h +23 -0
- rbtr_lang_c/tests/test_docstrings.py +43 -0
- rbtr_lang_c/tests/test_extraction.py +57 -0
- rbtr_lang_c/tests/test_samples.py +73 -0
- rbtr_lang_c-2026.7.0.dev0.dist-info/METADATA +8 -0
- rbtr_lang_c-2026.7.0.dev0.dist-info/RECORD +18 -0
- rbtr_lang_c-2026.7.0.dev0.dist-info/WHEEL +4 -0
- rbtr_lang_c-2026.7.0.dev0.dist-info/entry_points.txt +3 -0
rbtr_lang_c/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""C language plugin package."""
|
rbtr_lang_c/c.scm
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
; Top-level comments (C uses one `comment` type for `//` and `/* */`).
|
|
2
|
+
(comment) @comment
|
|
3
|
+
|
|
4
|
+
(function_definition
|
|
5
|
+
declarator: (function_declarator
|
|
6
|
+
declarator: (identifier) @_fn_name)) @function
|
|
7
|
+
|
|
8
|
+
(preproc_include
|
|
9
|
+
path: (system_lib_string) @_import_module) @import
|
|
10
|
+
|
|
11
|
+
(preproc_include
|
|
12
|
+
path: (string_literal) @_import_module) @import
|
|
13
|
+
|
|
14
|
+
(struct_specifier
|
|
15
|
+
name: (type_identifier) @_cls_name
|
|
16
|
+
body: (field_declaration_list)) @class
|
|
17
|
+
|
|
18
|
+
(union_specifier
|
|
19
|
+
name: (type_identifier) @_cls_name
|
|
20
|
+
body: (field_declaration_list)) @class
|
|
21
|
+
|
|
22
|
+
(enum_specifier
|
|
23
|
+
name: (type_identifier) @_cls_name
|
|
24
|
+
body: (enumerator_list)) @class
|
|
25
|
+
|
|
26
|
+
(enumerator
|
|
27
|
+
name: (identifier) @_var_name) @variable
|
|
28
|
+
|
|
29
|
+
(type_definition
|
|
30
|
+
declarator: (type_identifier) @_cls_name) @class
|
|
31
|
+
|
|
32
|
+
(type_definition
|
|
33
|
+
declarator: (function_declarator
|
|
34
|
+
declarator: (parenthesized_declarator
|
|
35
|
+
(pointer_declarator
|
|
36
|
+
declarator: (type_identifier) @_cls_name)))) @class
|
|
37
|
+
|
|
38
|
+
(preproc_function_def
|
|
39
|
+
name: (identifier) @_fn_name) @function
|
|
40
|
+
|
|
41
|
+
(preproc_def
|
|
42
|
+
name: (identifier) @_var_name) @variable
|
|
43
|
+
|
|
44
|
+
(translation_unit
|
|
45
|
+
(declaration
|
|
46
|
+
declarator: (function_declarator
|
|
47
|
+
declarator: (identifier) @_fn_name)) @function)
|
|
48
|
+
|
|
49
|
+
(translation_unit
|
|
50
|
+
(declaration
|
|
51
|
+
declarator: (init_declarator
|
|
52
|
+
declarator: (identifier) @_var_name)) @variable)
|
|
53
|
+
|
|
54
|
+
(translation_unit
|
|
55
|
+
(declaration
|
|
56
|
+
declarator: (pointer_declarator
|
|
57
|
+
declarator: (identifier) @_var_name)) @variable)
|
|
58
|
+
|
|
59
|
+
(translation_unit
|
|
60
|
+
(declaration
|
|
61
|
+
declarator: (init_declarator
|
|
62
|
+
declarator: (pointer_declarator
|
|
63
|
+
declarator: (identifier) @_var_name))) @variable)
|
rbtr_lang_c/plugin.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""C language plugin.
|
|
2
|
+
|
|
3
|
+
Provides symbol extraction (functions, function prototypes, structs,
|
|
4
|
+
unions, enums, typedefs, global variables, and function/object-like
|
|
5
|
+
macros) and include directive capture. Object-like macros and
|
|
6
|
+
pointer-declared globals are variables; function-like macros and
|
|
7
|
+
prototypes are functions.
|
|
8
|
+
|
|
9
|
+
Extracted chunks::
|
|
10
|
+
|
|
11
|
+
int add(int a, int b) { ... } → function "add", scope ""
|
|
12
|
+
struct Node { int value; }; → class "Node", scope ""
|
|
13
|
+
enum Color { RED, GREEN }; → class "Color", scope ""
|
|
14
|
+
typedef struct { ... } Point; → class "Point", scope ""
|
|
15
|
+
|
|
16
|
+
#include <stdio.h>
|
|
17
|
+
→ import, metadata {module: "stdio.h"}
|
|
18
|
+
#include "mylib.h"
|
|
19
|
+
→ import, metadata {module: "mylib.h"}
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from rbtr.languages.registration import LanguageRegistration, QueryExtraction, load_query
|
|
25
|
+
|
|
26
|
+
# ── Query ────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ── Plugin ───────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
c = LanguageRegistration(
|
|
33
|
+
id="c",
|
|
34
|
+
extensions=frozenset({".c", ".h"}),
|
|
35
|
+
grammar_module="tree_sitter_c",
|
|
36
|
+
extraction=QueryExtraction(
|
|
37
|
+
query=load_query(__package__, "c"),
|
|
38
|
+
),
|
|
39
|
+
source_roots=("", "include", "src"),
|
|
40
|
+
extraction_serial=4,
|
|
41
|
+
)
|
rbtr_lang_c/py.typed
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "4bbfada66e2e2dde",
|
|
4
|
+
"blob_sha": "sha1",
|
|
5
|
+
"file_path": "c.c",
|
|
6
|
+
"kind": "comment",
|
|
7
|
+
"name": "<anonymous>",
|
|
8
|
+
"scope": "",
|
|
9
|
+
"language": "c",
|
|
10
|
+
"content": "/* Greeter — format greetings for named recipients.\n *\n * The C plugin extracts functions, function prototypes, struct/union/\n * enum/typedef type definitions (as classes), top-level variables\n * (including pointer-declared globals), function/object-like macros,\n * and #include imports (system and local). */",
|
|
11
|
+
"line_start": 1,
|
|
12
|
+
"line_end": 6,
|
|
13
|
+
"metadata": {
|
|
14
|
+
"module": "",
|
|
15
|
+
"names": "",
|
|
16
|
+
"dots": "",
|
|
17
|
+
"language_hint": ""
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "ed19bac9e8771016",
|
|
22
|
+
"blob_sha": "sha1",
|
|
23
|
+
"file_path": "c.c",
|
|
24
|
+
"kind": "import",
|
|
25
|
+
"name": "#include <stdio.h>",
|
|
26
|
+
"scope": "",
|
|
27
|
+
"language": "c",
|
|
28
|
+
"content": "#include <stdio.h>\n",
|
|
29
|
+
"line_start": 8,
|
|
30
|
+
"line_end": 9,
|
|
31
|
+
"metadata": {
|
|
32
|
+
"module": "stdio.h",
|
|
33
|
+
"names": "",
|
|
34
|
+
"dots": "",
|
|
35
|
+
"language_hint": ""
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "f3bd03d43786cbdd",
|
|
40
|
+
"blob_sha": "sha1",
|
|
41
|
+
"file_path": "c.c",
|
|
42
|
+
"kind": "import",
|
|
43
|
+
"name": "#include \"greeter.h\"",
|
|
44
|
+
"scope": "",
|
|
45
|
+
"language": "c",
|
|
46
|
+
"content": "#include \"greeter.h\"\n",
|
|
47
|
+
"line_start": 9,
|
|
48
|
+
"line_end": 10,
|
|
49
|
+
"metadata": {
|
|
50
|
+
"module": "greeter.h",
|
|
51
|
+
"names": "",
|
|
52
|
+
"dots": "",
|
|
53
|
+
"language_hint": ""
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"id": "66295f4469f588b6",
|
|
58
|
+
"blob_sha": "sha1",
|
|
59
|
+
"file_path": "c.c",
|
|
60
|
+
"kind": "variable",
|
|
61
|
+
"name": "MAX_NAME",
|
|
62
|
+
"scope": "",
|
|
63
|
+
"language": "c",
|
|
64
|
+
"content": "#define MAX_NAME 64\n",
|
|
65
|
+
"line_start": 11,
|
|
66
|
+
"line_end": 12,
|
|
67
|
+
"metadata": {
|
|
68
|
+
"module": "",
|
|
69
|
+
"names": "",
|
|
70
|
+
"dots": "",
|
|
71
|
+
"language_hint": ""
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"id": "94c70ffbbc25dd51",
|
|
76
|
+
"blob_sha": "sha1",
|
|
77
|
+
"file_path": "c.c",
|
|
78
|
+
"kind": "function",
|
|
79
|
+
"name": "SQUARE",
|
|
80
|
+
"scope": "",
|
|
81
|
+
"language": "c",
|
|
82
|
+
"content": "#define SQUARE(x) ((x) * (x))\n",
|
|
83
|
+
"line_start": 12,
|
|
84
|
+
"line_end": 13,
|
|
85
|
+
"metadata": {
|
|
86
|
+
"module": "",
|
|
87
|
+
"names": "",
|
|
88
|
+
"dots": "",
|
|
89
|
+
"language_hint": ""
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "93f2130c32943142",
|
|
94
|
+
"blob_sha": "sha1",
|
|
95
|
+
"file_path": "c.c",
|
|
96
|
+
"kind": "variable",
|
|
97
|
+
"name": "greeter_count",
|
|
98
|
+
"scope": "",
|
|
99
|
+
"language": "c",
|
|
100
|
+
"content": "int greeter_count = 0;",
|
|
101
|
+
"line_start": 14,
|
|
102
|
+
"line_end": 14,
|
|
103
|
+
"metadata": {
|
|
104
|
+
"module": "",
|
|
105
|
+
"names": "",
|
|
106
|
+
"dots": "",
|
|
107
|
+
"language_hint": ""
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"id": "e3bef78ffe8ef8d8",
|
|
112
|
+
"blob_sha": "sha1",
|
|
113
|
+
"file_path": "c.c",
|
|
114
|
+
"kind": "comment",
|
|
115
|
+
"name": "<anonymous>",
|
|
116
|
+
"scope": "",
|
|
117
|
+
"language": "c",
|
|
118
|
+
"content": "// trailing comment: its own chunk, not folded",
|
|
119
|
+
"line_start": 14,
|
|
120
|
+
"line_end": 14,
|
|
121
|
+
"metadata": {
|
|
122
|
+
"module": "",
|
|
123
|
+
"names": "",
|
|
124
|
+
"dots": "",
|
|
125
|
+
"language_hint": ""
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"id": "0032d098456ec45a",
|
|
130
|
+
"blob_sha": "sha1",
|
|
131
|
+
"file_path": "c.c",
|
|
132
|
+
"kind": "variable",
|
|
133
|
+
"name": "default_prefix",
|
|
134
|
+
"scope": "",
|
|
135
|
+
"language": "c",
|
|
136
|
+
"content": "const char *default_prefix = \"Hello\";",
|
|
137
|
+
"line_start": 15,
|
|
138
|
+
"line_end": 15,
|
|
139
|
+
"metadata": {
|
|
140
|
+
"module": "",
|
|
141
|
+
"names": "",
|
|
142
|
+
"dots": "",
|
|
143
|
+
"language_hint": ""
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": "416366101cbeff9a",
|
|
148
|
+
"blob_sha": "sha1",
|
|
149
|
+
"file_path": "c.c",
|
|
150
|
+
"kind": "comment",
|
|
151
|
+
"name": "<anonymous>",
|
|
152
|
+
"scope": "",
|
|
153
|
+
"language": "c",
|
|
154
|
+
"content": "/* Standalone note, separated by blank lines from any definition. */\n/* Second line of the same block. */",
|
|
155
|
+
"line_start": 17,
|
|
156
|
+
"line_end": 18,
|
|
157
|
+
"metadata": {
|
|
158
|
+
"module": "",
|
|
159
|
+
"names": "",
|
|
160
|
+
"dots": "",
|
|
161
|
+
"language_hint": ""
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"id": "86c46ea843464bb6",
|
|
166
|
+
"blob_sha": "sha1",
|
|
167
|
+
"file_path": "c.c",
|
|
168
|
+
"kind": "class",
|
|
169
|
+
"name": "Greeter",
|
|
170
|
+
"scope": "",
|
|
171
|
+
"language": "c",
|
|
172
|
+
"content": "/* A greeter holding a prefix string. */\nstruct Greeter {\n const char *prefix;\n}",
|
|
173
|
+
"line_start": 20,
|
|
174
|
+
"line_end": 23,
|
|
175
|
+
"metadata": {
|
|
176
|
+
"module": "",
|
|
177
|
+
"names": "",
|
|
178
|
+
"dots": "",
|
|
179
|
+
"language_hint": ""
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "50ad4d624ef7d80b",
|
|
184
|
+
"blob_sha": "sha1",
|
|
185
|
+
"file_path": "c.c",
|
|
186
|
+
"kind": "class",
|
|
187
|
+
"name": "Greeter",
|
|
188
|
+
"scope": "",
|
|
189
|
+
"language": "c",
|
|
190
|
+
"content": "typedef struct Greeter Greeter;",
|
|
191
|
+
"line_start": 25,
|
|
192
|
+
"line_end": 25,
|
|
193
|
+
"metadata": {
|
|
194
|
+
"module": "",
|
|
195
|
+
"names": "",
|
|
196
|
+
"dots": "",
|
|
197
|
+
"language_hint": ""
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "7341787e58337d04",
|
|
202
|
+
"blob_sha": "sha1",
|
|
203
|
+
"file_path": "c.c",
|
|
204
|
+
"kind": "class",
|
|
205
|
+
"name": "GreetCallback",
|
|
206
|
+
"scope": "",
|
|
207
|
+
"language": "c",
|
|
208
|
+
"content": "/* A callback invoked for each formatted greeting. */\ntypedef void (*GreetCallback)(const char *line);",
|
|
209
|
+
"line_start": 27,
|
|
210
|
+
"line_end": 28,
|
|
211
|
+
"metadata": {
|
|
212
|
+
"module": "",
|
|
213
|
+
"names": "",
|
|
214
|
+
"dots": "",
|
|
215
|
+
"language_hint": ""
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"id": "e93571296c8a693c",
|
|
220
|
+
"blob_sha": "sha1",
|
|
221
|
+
"file_path": "c.c",
|
|
222
|
+
"kind": "class",
|
|
223
|
+
"name": "Payload",
|
|
224
|
+
"scope": "",
|
|
225
|
+
"language": "c",
|
|
226
|
+
"content": "/* A tagged greeting payload. */\nunion Payload {\n int code;\n const char *text;\n}",
|
|
227
|
+
"line_start": 30,
|
|
228
|
+
"line_end": 34,
|
|
229
|
+
"metadata": {
|
|
230
|
+
"module": "",
|
|
231
|
+
"names": "",
|
|
232
|
+
"dots": "",
|
|
233
|
+
"language_hint": ""
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"id": "41a8e4ce665cd2b7",
|
|
238
|
+
"blob_sha": "sha1",
|
|
239
|
+
"file_path": "c.c",
|
|
240
|
+
"kind": "class",
|
|
241
|
+
"name": "Locale",
|
|
242
|
+
"scope": "",
|
|
243
|
+
"language": "c",
|
|
244
|
+
"content": "enum Locale { LOCALE_EN, LOCALE_FR }",
|
|
245
|
+
"line_start": 36,
|
|
246
|
+
"line_end": 36,
|
|
247
|
+
"metadata": {
|
|
248
|
+
"module": "",
|
|
249
|
+
"names": "",
|
|
250
|
+
"dots": "",
|
|
251
|
+
"language_hint": ""
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
"id": "47db2d32016d125b",
|
|
256
|
+
"blob_sha": "sha1",
|
|
257
|
+
"file_path": "c.c",
|
|
258
|
+
"kind": "variable",
|
|
259
|
+
"name": "LOCALE_EN",
|
|
260
|
+
"scope": "",
|
|
261
|
+
"language": "c",
|
|
262
|
+
"content": "LOCALE_EN",
|
|
263
|
+
"line_start": 36,
|
|
264
|
+
"line_end": 36,
|
|
265
|
+
"metadata": {
|
|
266
|
+
"module": "",
|
|
267
|
+
"names": "",
|
|
268
|
+
"dots": "",
|
|
269
|
+
"language_hint": ""
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
"id": "bf9111abaa92b1f7",
|
|
274
|
+
"blob_sha": "sha1",
|
|
275
|
+
"file_path": "c.c",
|
|
276
|
+
"kind": "variable",
|
|
277
|
+
"name": "LOCALE_FR",
|
|
278
|
+
"scope": "",
|
|
279
|
+
"language": "c",
|
|
280
|
+
"content": "LOCALE_FR",
|
|
281
|
+
"line_start": 36,
|
|
282
|
+
"line_end": 36,
|
|
283
|
+
"metadata": {
|
|
284
|
+
"module": "",
|
|
285
|
+
"names": "",
|
|
286
|
+
"dots": "",
|
|
287
|
+
"language_hint": ""
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"id": "4e716afb57b39ec5",
|
|
292
|
+
"blob_sha": "sha1",
|
|
293
|
+
"file_path": "c.c",
|
|
294
|
+
"kind": "function",
|
|
295
|
+
"name": "greeter_default",
|
|
296
|
+
"scope": "",
|
|
297
|
+
"language": "c",
|
|
298
|
+
"content": "/* Build a greeter with the default prefix. */\nGreeter greeter_default(void);",
|
|
299
|
+
"line_start": 38,
|
|
300
|
+
"line_end": 39,
|
|
301
|
+
"metadata": {
|
|
302
|
+
"module": "",
|
|
303
|
+
"names": "",
|
|
304
|
+
"dots": "",
|
|
305
|
+
"language_hint": ""
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"id": "dc9b641d87a81ebc",
|
|
310
|
+
"blob_sha": "sha1",
|
|
311
|
+
"file_path": "c.c",
|
|
312
|
+
"kind": "function",
|
|
313
|
+
"name": "format_greeting",
|
|
314
|
+
"scope": "",
|
|
315
|
+
"language": "c",
|
|
316
|
+
"content": "/* Format a greeting for `name` into `buf`. */\nint format_greeting(const Greeter *g, const char *name, char *buf, int n) {\n return snprintf(buf, (size_t)n, \"%s, %s\", g->prefix, name);\n}",
|
|
317
|
+
"line_start": 41,
|
|
318
|
+
"line_end": 44,
|
|
319
|
+
"metadata": {
|
|
320
|
+
"module": "",
|
|
321
|
+
"names": "",
|
|
322
|
+
"dots": "",
|
|
323
|
+
"language_hint": ""
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"id": "02a12b65facd7a62",
|
|
328
|
+
"blob_sha": "sha1",
|
|
329
|
+
"file_path": "greeter.h",
|
|
330
|
+
"kind": "comment",
|
|
331
|
+
"name": "<anonymous>",
|
|
332
|
+
"scope": "",
|
|
333
|
+
"language": "c",
|
|
334
|
+
"content": "/* Header companion for c.c — declares the greeter API.\n *\n * Resolves the `#include \"greeter.h\"` edge and exercises C prototype and\n * type-definition capture (declared, not defined, here). */",
|
|
335
|
+
"line_start": 1,
|
|
336
|
+
"line_end": 4,
|
|
337
|
+
"metadata": {
|
|
338
|
+
"module": "",
|
|
339
|
+
"names": "",
|
|
340
|
+
"dots": "",
|
|
341
|
+
"language_hint": ""
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"id": "ab72e4cd48b2ee81",
|
|
346
|
+
"blob_sha": "sha1",
|
|
347
|
+
"file_path": "greeter.h",
|
|
348
|
+
"kind": "variable",
|
|
349
|
+
"name": "GREETER_H",
|
|
350
|
+
"scope": "",
|
|
351
|
+
"language": "c",
|
|
352
|
+
"content": "#define GREETER_H\n",
|
|
353
|
+
"line_start": 6,
|
|
354
|
+
"line_end": 7,
|
|
355
|
+
"metadata": {
|
|
356
|
+
"module": "",
|
|
357
|
+
"names": "",
|
|
358
|
+
"dots": "",
|
|
359
|
+
"language_hint": ""
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
"id": "c1960555fd4c603c",
|
|
364
|
+
"blob_sha": "sha1",
|
|
365
|
+
"file_path": "greeter.h",
|
|
366
|
+
"kind": "variable",
|
|
367
|
+
"name": "MAX_NAME",
|
|
368
|
+
"scope": "",
|
|
369
|
+
"language": "c",
|
|
370
|
+
"content": "#define MAX_NAME 64\n",
|
|
371
|
+
"line_start": 8,
|
|
372
|
+
"line_end": 9,
|
|
373
|
+
"metadata": {
|
|
374
|
+
"module": "",
|
|
375
|
+
"names": "",
|
|
376
|
+
"dots": "",
|
|
377
|
+
"language_hint": ""
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"id": "7d79f841f33998db",
|
|
382
|
+
"blob_sha": "sha1",
|
|
383
|
+
"file_path": "greeter.h",
|
|
384
|
+
"kind": "class",
|
|
385
|
+
"name": "Greeter",
|
|
386
|
+
"scope": "",
|
|
387
|
+
"language": "c",
|
|
388
|
+
"content": "/* A greeter holding a prefix string. */\nstruct Greeter {\n const char *prefix;\n}",
|
|
389
|
+
"line_start": 10,
|
|
390
|
+
"line_end": 13,
|
|
391
|
+
"metadata": {
|
|
392
|
+
"module": "",
|
|
393
|
+
"names": "",
|
|
394
|
+
"dots": "",
|
|
395
|
+
"language_hint": ""
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"id": "b262681e99ffd26f",
|
|
400
|
+
"blob_sha": "sha1",
|
|
401
|
+
"file_path": "greeter.h",
|
|
402
|
+
"kind": "class",
|
|
403
|
+
"name": "Greeter",
|
|
404
|
+
"scope": "",
|
|
405
|
+
"language": "c",
|
|
406
|
+
"content": "typedef struct Greeter Greeter;",
|
|
407
|
+
"line_start": 15,
|
|
408
|
+
"line_end": 15,
|
|
409
|
+
"metadata": {
|
|
410
|
+
"module": "",
|
|
411
|
+
"names": "",
|
|
412
|
+
"dots": "",
|
|
413
|
+
"language_hint": ""
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
"id": "8ff9df9b9b12f62c",
|
|
418
|
+
"blob_sha": "sha1",
|
|
419
|
+
"file_path": "greeter.h",
|
|
420
|
+
"kind": "comment",
|
|
421
|
+
"name": "<anonymous>",
|
|
422
|
+
"scope": "",
|
|
423
|
+
"language": "c",
|
|
424
|
+
"content": "/* Build a greeter with the default prefix. */",
|
|
425
|
+
"line_start": 17,
|
|
426
|
+
"line_end": 17,
|
|
427
|
+
"metadata": {
|
|
428
|
+
"module": "",
|
|
429
|
+
"names": "",
|
|
430
|
+
"dots": "",
|
|
431
|
+
"language_hint": ""
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
"id": "7232d67445ea06ec",
|
|
436
|
+
"blob_sha": "sha1",
|
|
437
|
+
"file_path": "greeter.h",
|
|
438
|
+
"kind": "comment",
|
|
439
|
+
"name": "<anonymous>",
|
|
440
|
+
"scope": "",
|
|
441
|
+
"language": "c",
|
|
442
|
+
"content": "/* Format a greeting for `name` into `buf`. */",
|
|
443
|
+
"line_start": 20,
|
|
444
|
+
"line_end": 20,
|
|
445
|
+
"metadata": {
|
|
446
|
+
"module": "",
|
|
447
|
+
"names": "",
|
|
448
|
+
"dots": "",
|
|
449
|
+
"language_hint": ""
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""C docstring-extraction test cases.
|
|
2
|
+
|
|
3
|
+
Each `@case` returns `(lang, source, symbol_name, snippet)` consumed by `test_docstrings.py`.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from pytest_cases import case
|
|
9
|
+
|
|
10
|
+
type DocstringCase = tuple[str, str, str, str]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@case(tags=["documented", "canonical", "exterior_doc"])
|
|
14
|
+
def case_c_doxygen_on_function() -> DocstringCase:
|
|
15
|
+
"""Canonical Doxygen `/** */` above a function."""
|
|
16
|
+
src = """\
|
|
17
|
+
/** Compute the sum. */
|
|
18
|
+
int add(int a, int b) { return a + b; }
|
|
19
|
+
"""
|
|
20
|
+
return "c", src, "add", "Compute the sum"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@case(tags=["documented", "canonical", "exterior_doc"])
|
|
24
|
+
def case_c_doxygen_on_struct() -> DocstringCase:
|
|
25
|
+
"""Doxygen above a struct."""
|
|
26
|
+
src = """\
|
|
27
|
+
/** Point in 2D space. */
|
|
28
|
+
struct Point { int x; int y; };
|
|
29
|
+
"""
|
|
30
|
+
return "c", src, "Point", "Point in 2D space"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@case(tags=["documented", "edge_case", "exterior_doc"])
|
|
34
|
+
def case_c_multi_line_doxygen() -> DocstringCase:
|
|
35
|
+
r"""Multi-line Doxygen with `\param` / `\return` tags."""
|
|
36
|
+
src = """\
|
|
37
|
+
/**
|
|
38
|
+
* Hash a buffer.
|
|
39
|
+
* \\param data the buffer
|
|
40
|
+
* \\return the hash
|
|
41
|
+
*/
|
|
42
|
+
int hash(const char *data) { return 0; }
|
|
43
|
+
"""
|
|
44
|
+
return "c", src, "hash", r"\return the hash"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@case(tags=["documented", "unconventional", "exterior_doc"])
|
|
48
|
+
def case_c_line_comment_run() -> DocstringCase:
|
|
49
|
+
"""Plain `//` comment run — common in embedded code where
|
|
50
|
+
Doxygen style is heavier than needed.
|
|
51
|
+
"""
|
|
52
|
+
src = """\
|
|
53
|
+
// Simple comment.
|
|
54
|
+
// Second line.
|
|
55
|
+
int foo(void) { return 0; }
|
|
56
|
+
"""
|
|
57
|
+
return "c", src, "foo", "Simple comment"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@case(tags=["undocumented", "no_docs"])
|
|
61
|
+
def case_c_fn_without_doc() -> DocstringCase:
|
|
62
|
+
src = """\
|
|
63
|
+
int bare(void) { return 0; }
|
|
64
|
+
"""
|
|
65
|
+
return "c", src, "bare", "PHANTOM_DOC_TEXT_SHOULD_NEVER_APPEAR"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@case(tags=["undocumented", "boundary_not_attached"])
|
|
69
|
+
def case_c_doc_detached_by_blank_line() -> DocstringCase:
|
|
70
|
+
"""Blank line breaks attachment."""
|
|
71
|
+
src = """\
|
|
72
|
+
/** Orphan. */
|
|
73
|
+
|
|
74
|
+
int later(void) { return 0; }
|
|
75
|
+
"""
|
|
76
|
+
return "c", src, "later", "Orphan"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@case(tags=["undocumented", "invalid"])
|
|
80
|
+
def case_c_doc_between_two_functions() -> DocstringCase:
|
|
81
|
+
"""Comment between two functions belongs to the later one."""
|
|
82
|
+
src = """\
|
|
83
|
+
int first(void) { return 0; }
|
|
84
|
+
|
|
85
|
+
/** Doc for second. */
|
|
86
|
+
int second(void) { return 0; }
|
|
87
|
+
"""
|
|
88
|
+
return "c", src, "first", "Doc for second"
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""C extraction test cases."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pytest_cases import case
|
|
6
|
+
|
|
7
|
+
type SymbolCase = tuple[str, str, list[tuple[str, str, str]]]
|
|
8
|
+
type ImportCase = tuple[str, str, dict[str, str]]
|
|
9
|
+
type MultiImportCase = tuple[str, str, int, list[dict[str, str]]]
|
|
10
|
+
type MixedCase = tuple[str, str, set[str], list[tuple[str, str]]]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@case(tags=["symbol"])
|
|
14
|
+
def case_c_function_basic() -> SymbolCase:
|
|
15
|
+
"""int add(int a, int b)."""
|
|
16
|
+
return "c", "int add(int a, int b) { return a + b; }\n", [("function", "add", "")]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@case(tags=["symbol"])
|
|
20
|
+
def case_c_function_void() -> SymbolCase:
|
|
21
|
+
"""void do_stuff(void)."""
|
|
22
|
+
return "c", "void do_stuff(void) { }\n", [("function", "do_stuff", "")]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@case(tags=["symbol"])
|
|
26
|
+
def case_c_function_static() -> SymbolCase:
|
|
27
|
+
"""static int helper(void)."""
|
|
28
|
+
return "c", "static int helper(void) { return 1; }\n", [("function", "helper", "")]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@case(tags=["symbol"])
|
|
32
|
+
def case_c_multiple_functions() -> SymbolCase:
|
|
33
|
+
"""Multiple C functions."""
|
|
34
|
+
src = """\
|
|
35
|
+
int foo(void) { return 0; }
|
|
36
|
+
void bar(void) { }
|
|
37
|
+
"""
|
|
38
|
+
return "c", src, [("function", "foo", ""), ("function", "bar", "")]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@case(tags=["symbol"])
|
|
42
|
+
def case_c_struct() -> SymbolCase:
|
|
43
|
+
"""struct Node."""
|
|
44
|
+
return "c", "struct Node { int value; };\n", [("class", "Node", "")]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@case(tags=["symbol"])
|
|
48
|
+
def case_c_enum() -> SymbolCase:
|
|
49
|
+
"""enum Color."""
|
|
50
|
+
return "c", "enum Color { RED, GREEN, BLUE };\n", [("class", "Color", "")]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@case(tags=["symbol"])
|
|
54
|
+
def case_c_typedef_struct() -> SymbolCase:
|
|
55
|
+
"""typedef struct { ... } Point."""
|
|
56
|
+
return "c", "typedef struct { int x; int y; } Point;\n", [("class", "Point", "")]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@case(tags=["symbol"])
|
|
60
|
+
def case_c_no_scope() -> SymbolCase:
|
|
61
|
+
"""C functions are never scoped — no classes."""
|
|
62
|
+
src = """\
|
|
63
|
+
struct S { int x; };
|
|
64
|
+
int func(void) { return 0; }
|
|
65
|
+
"""
|
|
66
|
+
return "c", src, [("function", "func", "")]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@case(tags=["import"])
|
|
70
|
+
def case_c_include_system() -> ImportCase:
|
|
71
|
+
"""#include <stdio.h>."""
|
|
72
|
+
return "c", "#include <stdio.h>\n", {"module": "stdio.h"}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@case(tags=["import"])
|
|
76
|
+
def case_c_include_local() -> ImportCase:
|
|
77
|
+
"""#include "mylib.h"."""
|
|
78
|
+
return "c", '#include "mylib.h"\n', {"module": "mylib.h"}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@case(tags=["import"])
|
|
82
|
+
def case_c_include_nested_path() -> ImportCase:
|
|
83
|
+
"""#include "utils/helpers.h"."""
|
|
84
|
+
return "c", '#include "utils/helpers.h"\n', {"module": "utils/helpers.h"}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@case(tags=["import"])
|
|
88
|
+
def case_c_include_system_nested() -> ImportCase:
|
|
89
|
+
"""#include <sys/types.h>."""
|
|
90
|
+
return "c", "#include <sys/types.h>\n", {"module": "sys/types.h"}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@case(tags=["multi_import"])
|
|
94
|
+
def case_c_multiple_includes() -> MultiImportCase:
|
|
95
|
+
"""Two include directives."""
|
|
96
|
+
src = """\
|
|
97
|
+
#include <stdlib.h>
|
|
98
|
+
#include "local.h"
|
|
99
|
+
"""
|
|
100
|
+
return "c", src, 2, [{"module": "stdlib.h"}, {"module": "local.h"}]
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@case(tags=["mixed"])
|
|
104
|
+
def case_c_full_file() -> MixedCase:
|
|
105
|
+
"""Realistic C file with Doxygen comments on every symbol.
|
|
106
|
+
|
|
107
|
+
Expected-kinds tuple unchanged.
|
|
108
|
+
"""
|
|
109
|
+
src = """\
|
|
110
|
+
#include <stdio.h>
|
|
111
|
+
#include "utils.h"
|
|
112
|
+
|
|
113
|
+
/** Runtime configuration for the parser. */
|
|
114
|
+
struct Config {
|
|
115
|
+
int timeout;
|
|
116
|
+
int retries;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/** Return status for parse operations. */
|
|
120
|
+
enum Status { OK, ERR };
|
|
121
|
+
|
|
122
|
+
/** Parse a config file from disk. */
|
|
123
|
+
int parse_config(const char *path) {
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Release parser-owned resources. */
|
|
128
|
+
static void cleanup(void) {
|
|
129
|
+
}
|
|
130
|
+
"""
|
|
131
|
+
return "c", src, {"import", "class", "function"}, []
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@case(tags=["symbol"])
|
|
135
|
+
def case_c_global() -> SymbolCase:
|
|
136
|
+
"""File-scope global with initialiser."""
|
|
137
|
+
return "c", "int g = 5;\n", [("variable", "g", "")]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* Greeter — format greetings for named recipients.
|
|
2
|
+
*
|
|
3
|
+
* The C plugin extracts functions, function prototypes, struct/union/
|
|
4
|
+
* enum/typedef type definitions (as classes), top-level variables
|
|
5
|
+
* (including pointer-declared globals), function/object-like macros,
|
|
6
|
+
* and #include imports (system and local). */
|
|
7
|
+
|
|
8
|
+
#include <stdio.h>
|
|
9
|
+
#include "greeter.h"
|
|
10
|
+
|
|
11
|
+
#define MAX_NAME 64
|
|
12
|
+
#define SQUARE(x) ((x) * (x))
|
|
13
|
+
|
|
14
|
+
int greeter_count = 0; // trailing comment: its own chunk, not folded
|
|
15
|
+
const char *default_prefix = "Hello";
|
|
16
|
+
|
|
17
|
+
/* Standalone note, separated by blank lines from any definition. */
|
|
18
|
+
/* Second line of the same block. */
|
|
19
|
+
|
|
20
|
+
/* A greeter holding a prefix string. */
|
|
21
|
+
struct Greeter {
|
|
22
|
+
const char *prefix;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
typedef struct Greeter Greeter;
|
|
26
|
+
|
|
27
|
+
/* A callback invoked for each formatted greeting. */
|
|
28
|
+
typedef void (*GreetCallback)(const char *line);
|
|
29
|
+
|
|
30
|
+
/* A tagged greeting payload. */
|
|
31
|
+
union Payload {
|
|
32
|
+
int code;
|
|
33
|
+
const char *text;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
enum Locale { LOCALE_EN, LOCALE_FR };
|
|
37
|
+
|
|
38
|
+
/* Build a greeter with the default prefix. */
|
|
39
|
+
Greeter greeter_default(void);
|
|
40
|
+
|
|
41
|
+
/* Format a greeting for `name` into `buf`. */
|
|
42
|
+
int format_greeting(const Greeter *g, const char *name, char *buf, int n) {
|
|
43
|
+
return snprintf(buf, (size_t)n, "%s, %s", g->prefix, name);
|
|
44
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* Header companion for c.c — declares the greeter API.
|
|
2
|
+
*
|
|
3
|
+
* Resolves the `#include "greeter.h"` edge and exercises C prototype and
|
|
4
|
+
* type-definition capture (declared, not defined, here). */
|
|
5
|
+
#ifndef GREETER_H
|
|
6
|
+
#define GREETER_H
|
|
7
|
+
|
|
8
|
+
#define MAX_NAME 64
|
|
9
|
+
|
|
10
|
+
/* A greeter holding a prefix string. */
|
|
11
|
+
struct Greeter {
|
|
12
|
+
const char *prefix;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
typedef struct Greeter Greeter;
|
|
16
|
+
|
|
17
|
+
/* Build a greeter with the default prefix. */
|
|
18
|
+
Greeter greeter_default(void);
|
|
19
|
+
|
|
20
|
+
/* Format a greeting for `name` into `buf`. */
|
|
21
|
+
int format_greeting(const Greeter *g, const char *name, char *buf, int n);
|
|
22
|
+
|
|
23
|
+
#endif
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""C doc-comment extraction (C is exterior-doc: leading comments attach via the sibling walk)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pytest_cases import parametrize_with_cases
|
|
6
|
+
|
|
7
|
+
from rbtr.git import FileEntry
|
|
8
|
+
from rbtr.languages.extract import extract_file
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@parametrize_with_cases(
|
|
12
|
+
"lang, source, name, snippet", cases=".cases_docstrings", has_tag="documented"
|
|
13
|
+
)
|
|
14
|
+
def test_documented_chunk_includes_doc_text(
|
|
15
|
+
lang: str, source: str, name: str, snippet: str
|
|
16
|
+
) -> None:
|
|
17
|
+
"""By default the chunk content carries the symbol's docs."""
|
|
18
|
+
chunks = extract_file(FileEntry("input", "sha1", source.encode()), lang)
|
|
19
|
+
chunk = next(c for c in chunks if c.name == name)
|
|
20
|
+
assert snippet in chunk.content, f"expected {snippet!r} in {lang}.{name}: {chunk.content!r}"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@parametrize_with_cases(
|
|
24
|
+
"lang, source, name, snippet", cases=".cases_docstrings", has_tag="undocumented"
|
|
25
|
+
)
|
|
26
|
+
def test_no_phantom_documentation(lang: str, source: str, name: str, snippet: str) -> None:
|
|
27
|
+
"""Symbols without documentation do not gain any in content."""
|
|
28
|
+
chunks = extract_file(FileEntry("input", "sha1", source.encode()), lang)
|
|
29
|
+
chunk = next(c for c in chunks if c.name == name)
|
|
30
|
+
assert snippet not in chunk.content, (
|
|
31
|
+
f"unexpected {snippet!r} in {lang}.{name}: {chunk.content!r}"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@parametrize_with_cases(
|
|
36
|
+
"lang, source, name, snippet", cases=".cases_docstrings", has_tag="exterior_doc"
|
|
37
|
+
)
|
|
38
|
+
def test_leading_doc_folds_into_symbol(lang: str, source: str, name: str, snippet: str) -> None:
|
|
39
|
+
"""A leading comment block folds into its symbol's chunk content."""
|
|
40
|
+
chunk = next(
|
|
41
|
+
c for c in extract_file(FileEntry("input", "sha1", source.encode()), lang) if c.name == name
|
|
42
|
+
)
|
|
43
|
+
assert snippet in chunk.content
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""C extraction tests (cases in `cases_extraction.py`)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pytest_cases import parametrize_with_cases
|
|
6
|
+
|
|
7
|
+
from rbtr.git import FileEntry
|
|
8
|
+
from rbtr.index.models import ChunkKind, ImportMeta
|
|
9
|
+
from rbtr.languages.extract import extract_file
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@parametrize_with_cases("lang, source, expected", cases=".cases_extraction", has_tag="symbol")
|
|
13
|
+
def test_extracts_expected_symbols(lang: str, source: str, expected: list) -> None:
|
|
14
|
+
"""Each expected (kind, name, scope) tuple appears in the output."""
|
|
15
|
+
chunks = extract_file(FileEntry("input", "sha1", source.encode()), lang)
|
|
16
|
+
symbols = [(c.kind, c.name, c.scope) for c in chunks]
|
|
17
|
+
for exp in expected:
|
|
18
|
+
assert exp in symbols, f"expected {exp} not found in {symbols}"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@parametrize_with_cases(
|
|
22
|
+
"lang, source, expected_kinds, expected_methods", cases=".cases_extraction", has_tag="mixed"
|
|
23
|
+
)
|
|
24
|
+
def test_extracts_all_expected_kinds(
|
|
25
|
+
lang: str, source: str, expected_kinds: set[str], expected_methods: list[tuple[str, str]]
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Realistic source produces all expected chunk kinds and method scoping."""
|
|
28
|
+
chunks = extract_file(FileEntry("input", "sha1", source.encode()), lang)
|
|
29
|
+
kinds = {c.kind for c in chunks}
|
|
30
|
+
for kind in expected_kinds:
|
|
31
|
+
assert kind in kinds, f"expected kind {kind!r} not in {kinds}"
|
|
32
|
+
methods = [(c.name, c.scope) for c in chunks if c.kind == ChunkKind.METHOD]
|
|
33
|
+
for name, scope in expected_methods:
|
|
34
|
+
assert (name, scope) in methods, f"expected method ({name}, {scope}) not in {methods}"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@parametrize_with_cases("lang, source, expected", cases=".cases_extraction", has_tag="import")
|
|
38
|
+
def test_extracts_import_metadata(lang: str, source: str, expected: dict) -> None:
|
|
39
|
+
"""First import chunk has the expected metadata."""
|
|
40
|
+
chunks = extract_file(FileEntry("input", "sha1", source.encode()), lang)
|
|
41
|
+
imports = [c for c in chunks if c.kind == ChunkKind.IMPORT]
|
|
42
|
+
assert len(imports) >= 1, f"no import chunks extracted from {source!r}"
|
|
43
|
+
assert imports[0].metadata == ImportMeta(**expected)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@parametrize_with_cases(
|
|
47
|
+
"lang, source, count, metadata_list", cases=".cases_extraction", has_tag="multi_import"
|
|
48
|
+
)
|
|
49
|
+
def test_extracts_multi_import(
|
|
50
|
+
lang: str, source: str, count: int, metadata_list: list[dict]
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Multiple imports have correct count and per-import metadata."""
|
|
53
|
+
chunks = extract_file(FileEntry("input", "sha1", source.encode()), lang)
|
|
54
|
+
imports = [c for c in chunks if c.kind == ChunkKind.IMPORT]
|
|
55
|
+
assert len(imports) == count
|
|
56
|
+
for imp, expected in zip(imports, metadata_list, strict=True):
|
|
57
|
+
assert imp.metadata == ImportMeta(**expected)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""C sample extraction: the `samples/c/` project through the real pipeline."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from tree_sitter import Parser
|
|
10
|
+
|
|
11
|
+
from rbtr.git import FileEntry
|
|
12
|
+
from rbtr.index.models import Chunk, ChunkKind, Edge
|
|
13
|
+
from rbtr.languages.edges import build_resolution_map, infer_import_edges
|
|
14
|
+
from rbtr.languages.extract import extract_file
|
|
15
|
+
from rbtr.languages.manager import get_manager
|
|
16
|
+
from rbtr.testing import render_edges
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from syrupy.assertion import SnapshotAssertion
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture
|
|
23
|
+
def project() -> list[tuple[str, str]]:
|
|
24
|
+
root = Path(__file__).parent / "samples" / "c"
|
|
25
|
+
return [
|
|
26
|
+
(str(p.relative_to(root)), p.read_text()) for p in sorted(root.rglob("*")) if p.is_file()
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.fixture
|
|
31
|
+
def chunks(project: list[tuple[str, str]]) -> list[Chunk]:
|
|
32
|
+
manager = get_manager()
|
|
33
|
+
out: list[Chunk] = []
|
|
34
|
+
for path, text in project:
|
|
35
|
+
lang = manager.detect_language(path) or "c"
|
|
36
|
+
out.extend(extract_file(FileEntry(path, "sha1", text.encode()), lang))
|
|
37
|
+
return out
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@pytest.fixture
|
|
41
|
+
def edges(project: list[tuple[str, str]], chunks: list[Chunk]) -> list[Edge]:
|
|
42
|
+
manager = get_manager()
|
|
43
|
+
repo_files = {path for path, _ in project}
|
|
44
|
+
return infer_import_edges(chunks, repo_files, build_resolution_map(manager))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_emits_expected_kinds(chunks: list[Chunk]) -> None:
|
|
48
|
+
kinds = {c.kind for c in chunks}
|
|
49
|
+
assert {
|
|
50
|
+
ChunkKind.FUNCTION,
|
|
51
|
+
ChunkKind.CLASS,
|
|
52
|
+
ChunkKind.VARIABLE,
|
|
53
|
+
ChunkKind.IMPORT,
|
|
54
|
+
ChunkKind.COMMENT,
|
|
55
|
+
} <= kinds
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_parses_cleanly(project: list[tuple[str, str]]) -> None:
|
|
59
|
+
manager = get_manager()
|
|
60
|
+
for path, text in project:
|
|
61
|
+
grammar = manager.grammar(manager.detect_language(path) or "c")
|
|
62
|
+
assert grammar is not None
|
|
63
|
+
assert not Parser(grammar).parse(text.encode()).root_node.has_error, path
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_extraction_matches_snapshot(chunks: list[Chunk], snapshot_json: SnapshotAssertion) -> None:
|
|
67
|
+
assert chunks == snapshot_json
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_edges_match_snapshot(
|
|
71
|
+
chunks: list[Chunk], edges: list[Edge], snapshot_json: SnapshotAssertion
|
|
72
|
+
) -> None:
|
|
73
|
+
assert render_edges(edges, chunks) == snapshot_json
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
rbtr_lang_c/__init__.py,sha256=W3rb2Hb3BOLyp3wleR_VPsXyJGMQblamkeX7hbqoRlo,33
|
|
2
|
+
rbtr_lang_c/c.scm,sha256=AJ1PhPeS3Uec-wNBY3AcHjURJHhPQoI_4Zs05sdI9Po,1581
|
|
3
|
+
rbtr_lang_c/plugin.py,sha256=a0sSTlDv_7D2InnxHteDBUA0euL_3yTLrg8JPU8-imA,1527
|
|
4
|
+
rbtr_lang_c/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
rbtr_lang_c/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
rbtr_lang_c/tests/__snapshots__/test_samples/test_edges_match_snapshot.json,sha256=l8QD-x3nsCmTkqN6G3hcZZPac-zVk5gQFhKYo7TtfHw,266
|
|
7
|
+
rbtr_lang_c/tests/__snapshots__/test_samples/test_extraction_matches_snapshot.json,sha256=zSFlCZqJ1NVo5C6tI_CXkxagbYDMSjaJJpn-YokLWYs,10319
|
|
8
|
+
rbtr_lang_c/tests/cases_docstrings.py,sha256=-JGathdyZlZURDl6iI5CumMufpZ7Y6SUouQVtMshyRw,2296
|
|
9
|
+
rbtr_lang_c/tests/cases_extraction.py,sha256=1AkfnBFoJR71WY-rc-S81yX8IOMiZJWNx73e7Qc0m7c,3590
|
|
10
|
+
rbtr_lang_c/tests/samples/c/c.c,sha256=XBE6ciJZCeSlFVz6hXuJE0o9CNROGJkn5ORMUtbjVMk,1240
|
|
11
|
+
rbtr_lang_c/tests/samples/c/greeter.h,sha256=KVCbgHbINca9FWposImUiESk1CfE7hiC9f7TqHFn_AE,582
|
|
12
|
+
rbtr_lang_c/tests/test_docstrings.py,sha256=Sr69qUSWgy4Jb_-4dmi9iNzy2dSTX69nOr9P8rE0urU,1746
|
|
13
|
+
rbtr_lang_c/tests/test_extraction.py,sha256=oBQPoZSXfeIZ0EWHR8_-TYASKaso7hiV9s1eZsTPzaA,2651
|
|
14
|
+
rbtr_lang_c/tests/test_samples.py,sha256=PNvepW1j9oBL4pYtD-T4mJt0ACJP36EUDyCNGeoW7sw,2259
|
|
15
|
+
rbtr_lang_c-2026.7.0.dev0.dist-info/WHEEL,sha256=CoDSoyhtC_eO_tlxRYzsTraPv1fPJRXFx91k6ISeAvA,81
|
|
16
|
+
rbtr_lang_c-2026.7.0.dev0.dist-info/entry_points.txt,sha256=9LThxwGT0btaR93-FSKuhnw1Lxlt8DE1qgN2mz-znOw,43
|
|
17
|
+
rbtr_lang_c-2026.7.0.dev0.dist-info/METADATA,sha256=Uy5NlqnJ4tH-vxEHOHeGGP9_8PYo0U_LJ3Zktjenuac,211
|
|
18
|
+
rbtr_lang_c-2026.7.0.dev0.dist-info/RECORD,,
|