rbtr-lang-go 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.
@@ -0,0 +1 @@
1
+ """Go language plugin package."""
rbtr_lang_go/go.scm ADDED
@@ -0,0 +1,53 @@
1
+ ; Top-level comments (Go uses one `comment` type for `//` and `/* */`).
2
+ ; The engine groups these and folds a block flush before a symbol into it.
3
+ (comment) @comment
4
+
5
+ (function_declaration
6
+ name: (identifier) @_fn_name) @function
7
+
8
+ (method_declaration
9
+ receiver: (parameter_list
10
+ (parameter_declaration
11
+ type: [
12
+ (type_identifier) @_scope
13
+ (pointer_type (type_identifier) @_scope)
14
+ ]))
15
+ name: (field_identifier) @_method_name) @method
16
+
17
+ (type_declaration
18
+ (type_spec
19
+ name: (type_identifier) @_cls_name)) @class
20
+
21
+ (type_spec
22
+ (interface_type
23
+ (method_elem
24
+ name: (field_identifier) @_method_name) @method))
25
+
26
+ (type_declaration
27
+ (type_alias
28
+ name: (type_identifier) @_cls_name)) @class
29
+
30
+ (import_declaration
31
+ (import_spec
32
+ path: (interpreted_string_literal) @_import_module) @import)
33
+
34
+ (import_declaration
35
+ (import_spec_list
36
+ (import_spec
37
+ path: (interpreted_string_literal) @_import_module) @import))
38
+
39
+ (source_file
40
+ (var_declaration
41
+ (var_spec
42
+ name: (identifier) @_var_name) @variable))
43
+
44
+ (source_file
45
+ (const_declaration
46
+ (const_spec
47
+ name: (identifier) @_var_name) @variable))
48
+
49
+ (source_file
50
+ (var_declaration
51
+ (var_spec_list
52
+ (var_spec
53
+ name: (identifier) @_var_name) @variable)))
rbtr_lang_go/plugin.py ADDED
@@ -0,0 +1,38 @@
1
+ """Go language plugin.
2
+
3
+ Provides full support: functions, methods, type declarations,
4
+ and import extraction.
5
+
6
+ Extracted chunks::
7
+
8
+ func hello() {} → function "hello", scope ""
9
+ func (u User) Name() string {} → method "Name", scope ""
10
+ type User struct { ... } → class "User", scope ""
11
+ type Reader interface { ... } → class "Reader", scope ""
12
+
13
+ import "fmt"
14
+ → import, metadata {module: "fmt"}
15
+ import ("fmt" "os/exec")
16
+ → 2 import chunks: {module: "fmt"}, {module: "os/exec"}
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from rbtr.languages.registration import LanguageRegistration, QueryExtraction, load_query
22
+
23
+ # ── Query ────────────────────────────────────────────────────────────
24
+
25
+
26
+ # ── Plugin ───────────────────────────────────────────────────────────
27
+
28
+
29
+ go = LanguageRegistration(
30
+ id="go",
31
+ extensions=frozenset({".go"}),
32
+ grammar_module="tree_sitter_go",
33
+ extraction=QueryExtraction(
34
+ query=load_query(__package__, "go"),
35
+ scope_types=frozenset({"type_spec"}),
36
+ ),
37
+ extraction_serial=4,
38
+ )
rbtr_lang_go/py.typed ADDED
File without changes
File without changes
@@ -0,0 +1,3 @@
1
+ [
2
+ "go.go::\"greeter/util\" -> greeter/util.go::Trim [imports]"
3
+ ]
@@ -0,0 +1,308 @@
1
+ [
2
+ {
3
+ "id": "d1f4278c0ca4e638",
4
+ "blob_sha": "sha1",
5
+ "file_path": "go.go",
6
+ "kind": "comment",
7
+ "name": "<anonymous>",
8
+ "scope": "",
9
+ "language": "go",
10
+ "content": "// Package greet formats greetings for named recipients.\n//\n// The Go plugin extracts functions, methods (functions with a\n// receiver), type declarations (struct, interface, and alias, as\n// classes), const/var declarations (as variables), and imports.",
11
+ "line_start": 1,
12
+ "line_end": 5,
13
+ "metadata": {
14
+ "module": "",
15
+ "names": "",
16
+ "dots": "",
17
+ "language_hint": ""
18
+ }
19
+ },
20
+ {
21
+ "id": "617a74fefdf94501",
22
+ "blob_sha": "sha1",
23
+ "file_path": "go.go",
24
+ "kind": "import",
25
+ "name": "\"fmt\"",
26
+ "scope": "",
27
+ "language": "go",
28
+ "content": "\"fmt\"",
29
+ "line_start": 9,
30
+ "line_end": 9,
31
+ "metadata": {
32
+ "module": "fmt",
33
+ "names": "",
34
+ "dots": "",
35
+ "language_hint": ""
36
+ }
37
+ },
38
+ {
39
+ "id": "22c93a3cd18d6a4e",
40
+ "blob_sha": "sha1",
41
+ "file_path": "go.go",
42
+ "kind": "import",
43
+ "name": "\"greeter/util\"",
44
+ "scope": "",
45
+ "language": "go",
46
+ "content": "\"greeter/util\"",
47
+ "line_start": 11,
48
+ "line_end": 11,
49
+ "metadata": {
50
+ "module": "greeter/util",
51
+ "names": "",
52
+ "dots": "",
53
+ "language_hint": ""
54
+ }
55
+ },
56
+ {
57
+ "id": "1bc7a9beaa5abc1f",
58
+ "blob_sha": "sha1",
59
+ "file_path": "go.go",
60
+ "kind": "comment",
61
+ "name": "<anonymous>",
62
+ "scope": "",
63
+ "language": "go",
64
+ "content": "// DefaultGreeting is the fallback prefix.",
65
+ "line_start": 14,
66
+ "line_end": 14,
67
+ "metadata": {
68
+ "module": "",
69
+ "names": "",
70
+ "dots": "",
71
+ "language_hint": ""
72
+ }
73
+ },
74
+ {
75
+ "id": "6088984e54e72e54",
76
+ "blob_sha": "sha1",
77
+ "file_path": "go.go",
78
+ "kind": "variable",
79
+ "name": "DefaultGreeting",
80
+ "scope": "",
81
+ "language": "go",
82
+ "content": "DefaultGreeting = \"Hello\"",
83
+ "line_start": 15,
84
+ "line_end": 15,
85
+ "metadata": {
86
+ "module": "",
87
+ "names": "",
88
+ "dots": "",
89
+ "language_hint": ""
90
+ }
91
+ },
92
+ {
93
+ "id": "8682e0e6778060e5",
94
+ "blob_sha": "sha1",
95
+ "file_path": "go.go",
96
+ "kind": "variable",
97
+ "name": "fallbackLocale",
98
+ "scope": "",
99
+ "language": "go",
100
+ "content": "fallbackLocale = \"en\"",
101
+ "line_start": 17,
102
+ "line_end": 17,
103
+ "metadata": {
104
+ "module": "",
105
+ "names": "",
106
+ "dots": "",
107
+ "language_hint": ""
108
+ }
109
+ },
110
+ {
111
+ "id": "6cf50d84e9893957",
112
+ "blob_sha": "sha1",
113
+ "file_path": "go.go",
114
+ "kind": "comment",
115
+ "name": "<anonymous>",
116
+ "scope": "",
117
+ "language": "go",
118
+ "content": "// trailing comment: its own chunk, not folded",
119
+ "line_start": 17,
120
+ "line_end": 17,
121
+ "metadata": {
122
+ "module": "",
123
+ "names": "",
124
+ "dots": "",
125
+ "language_hint": ""
126
+ }
127
+ },
128
+ {
129
+ "id": "6d998cbd76a60714",
130
+ "blob_sha": "sha1",
131
+ "file_path": "go.go",
132
+ "kind": "class",
133
+ "name": "Temperature",
134
+ "scope": "",
135
+ "language": "go",
136
+ "content": "// Temperature is an alias for the underlying float type.\ntype Temperature = float64",
137
+ "line_start": 19,
138
+ "line_end": 20,
139
+ "metadata": {
140
+ "module": "",
141
+ "names": "",
142
+ "dots": "",
143
+ "language_hint": ""
144
+ }
145
+ },
146
+ {
147
+ "id": "3f6b670c5127f21d",
148
+ "blob_sha": "sha1",
149
+ "file_path": "go.go",
150
+ "kind": "class",
151
+ "name": "Greeter",
152
+ "scope": "",
153
+ "language": "go",
154
+ "content": "// Greeter holds a greeting prefix.\ntype Greeter struct {\n\tPrefix string\n}",
155
+ "line_start": 22,
156
+ "line_end": 25,
157
+ "metadata": {
158
+ "module": "",
159
+ "names": "",
160
+ "dots": "",
161
+ "language_hint": ""
162
+ }
163
+ },
164
+ {
165
+ "id": "373545cefb441d30",
166
+ "blob_sha": "sha1",
167
+ "file_path": "go.go",
168
+ "kind": "class",
169
+ "name": "Formatter",
170
+ "scope": "",
171
+ "language": "go",
172
+ "content": "// Formatter renders a greeting for a name.\ntype Formatter interface {\n\tFormat(name string) string\n}",
173
+ "line_start": 27,
174
+ "line_end": 30,
175
+ "metadata": {
176
+ "module": "",
177
+ "names": "",
178
+ "dots": "",
179
+ "language_hint": ""
180
+ }
181
+ },
182
+ {
183
+ "id": "7c2d2b8469f4c6b9",
184
+ "blob_sha": "sha1",
185
+ "file_path": "go.go",
186
+ "kind": "method",
187
+ "name": "Format",
188
+ "scope": "Formatter",
189
+ "language": "go",
190
+ "content": "Format(name string) string",
191
+ "line_start": 29,
192
+ "line_end": 29,
193
+ "metadata": {
194
+ "module": "",
195
+ "names": "",
196
+ "dots": "",
197
+ "language_hint": ""
198
+ }
199
+ },
200
+ {
201
+ "id": "77923e25be8f065d",
202
+ "blob_sha": "sha1",
203
+ "file_path": "go.go",
204
+ "kind": "comment",
205
+ "name": "<anonymous>",
206
+ "scope": "",
207
+ "language": "go",
208
+ "content": "// Standalone note, separated by blank lines from any definition.\n// Second line of the same block.",
209
+ "line_start": 32,
210
+ "line_end": 33,
211
+ "metadata": {
212
+ "module": "",
213
+ "names": "",
214
+ "dots": "",
215
+ "language_hint": ""
216
+ }
217
+ },
218
+ {
219
+ "id": "d49eb7eef5c71c0f",
220
+ "blob_sha": "sha1",
221
+ "file_path": "go.go",
222
+ "kind": "method",
223
+ "name": "Greet",
224
+ "scope": "Greeter",
225
+ "language": "go",
226
+ "content": "// Greet greets a single recipient.\nfunc (g Greeter) Greet(name string) string {\n\treturn fmt.Sprintf(\"%s, %s\", g.Prefix, util.Trim(name))\n}",
227
+ "line_start": 35,
228
+ "line_end": 38,
229
+ "metadata": {
230
+ "module": "",
231
+ "names": "",
232
+ "dots": "",
233
+ "language_hint": ""
234
+ }
235
+ },
236
+ {
237
+ "id": "8355f60b7f62d374",
238
+ "blob_sha": "sha1",
239
+ "file_path": "go.go",
240
+ "kind": "function",
241
+ "name": "FormatGreeting",
242
+ "scope": "",
243
+ "language": "go",
244
+ "content": "// FormatGreeting formats a greeting via a Greeter.\nfunc FormatGreeting(g Greeter, name string) string {\n\treturn g.Greet(name)\n}",
245
+ "line_start": 40,
246
+ "line_end": 43,
247
+ "metadata": {
248
+ "module": "",
249
+ "names": "",
250
+ "dots": "",
251
+ "language_hint": ""
252
+ }
253
+ },
254
+ {
255
+ "id": "19cd38988ba480c4",
256
+ "blob_sha": "sha1",
257
+ "file_path": "greeter/util.go",
258
+ "kind": "comment",
259
+ "name": "<anonymous>",
260
+ "scope": "",
261
+ "language": "go",
262
+ "content": "// Package util holds greeting helpers.",
263
+ "line_start": 1,
264
+ "line_end": 1,
265
+ "metadata": {
266
+ "module": "",
267
+ "names": "",
268
+ "dots": "",
269
+ "language_hint": ""
270
+ }
271
+ },
272
+ {
273
+ "id": "d2279d95b96822bc",
274
+ "blob_sha": "sha1",
275
+ "file_path": "greeter/util.go",
276
+ "kind": "import",
277
+ "name": "\"strings\"",
278
+ "scope": "",
279
+ "language": "go",
280
+ "content": "\"strings\"",
281
+ "line_start": 4,
282
+ "line_end": 4,
283
+ "metadata": {
284
+ "module": "strings",
285
+ "names": "",
286
+ "dots": "",
287
+ "language_hint": ""
288
+ }
289
+ },
290
+ {
291
+ "id": "5d95fbaf4ba5f22b",
292
+ "blob_sha": "sha1",
293
+ "file_path": "greeter/util.go",
294
+ "kind": "function",
295
+ "name": "Trim",
296
+ "scope": "",
297
+ "language": "go",
298
+ "content": "// Trim removes surrounding whitespace from a name.\nfunc Trim(name string) string {\n\treturn strings.TrimSpace(name)\n}",
299
+ "line_start": 6,
300
+ "line_end": 9,
301
+ "metadata": {
302
+ "module": "",
303
+ "names": "",
304
+ "dots": "",
305
+ "language_hint": ""
306
+ }
307
+ }
308
+ ]
@@ -0,0 +1,140 @@
1
+ """Go 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_go_doc_comment_on_function() -> DocstringCase:
15
+ """Canonical Go doc comment above `func`."""
16
+ src = """\
17
+ package main
18
+
19
+ // Greet says hello.
20
+ func Greet() {}
21
+ """
22
+ return "go", src, "Greet", "Greet says hello"
23
+
24
+
25
+ @case(tags=["documented", "canonical", "exterior_doc"])
26
+ def case_go_doc_comment_on_type() -> DocstringCase:
27
+ """Doc comment above a `type` declaration."""
28
+ src = """\
29
+ package main
30
+
31
+ // Widget is a UI element.
32
+ type Widget struct {
33
+ name string
34
+ }
35
+ """
36
+ return "go", src, "Widget", "Widget is a UI element"
37
+
38
+
39
+ @case(tags=["documented", "canonical", "exterior_doc"])
40
+ def case_go_doc_comment_on_method() -> DocstringCase:
41
+ """Doc comment above a method receiver."""
42
+ src = """\
43
+ package main
44
+
45
+ type T struct{}
46
+
47
+ // Do performs the action.
48
+ func (t *T) Do() {}
49
+ """
50
+ return "go", src, "Do", "Do performs the action"
51
+
52
+
53
+ @case(tags=["documented", "canonical", "exterior_doc"])
54
+ def case_go_multi_line_doc_comment() -> DocstringCase:
55
+ """Multi-line `//` doc-comment run."""
56
+ src = """\
57
+ package main
58
+
59
+ // Compute executes the pipeline.
60
+ //
61
+ // It returns an error if the inputs do not validate.
62
+ func Compute() error { return nil }
63
+ """
64
+ return "go", src, "Compute", "It returns an error if the inputs"
65
+
66
+
67
+ @case(tags=["documented", "edge_case", "exterior_doc"])
68
+ def case_go_block_comment() -> DocstringCase:
69
+ """`/* ... */` block comment as Go-doc. Supported by
70
+ `go doc` but rare.
71
+ """
72
+ src = """\
73
+ package main
74
+
75
+ /* Block doc above foo.
76
+ Continues here. */
77
+ func Foo() {}
78
+ """
79
+ return "go", src, "Foo", "Block doc above foo"
80
+
81
+
82
+ @case(tags=["documented", "unconventional", "exterior_doc"])
83
+ def case_go_non_godoc_style_comment() -> DocstringCase:
84
+ """Comment that does *not* begin with the symbol's name.
85
+ `go doc` would warn, but rbtr leans toward flexibility and
86
+ attaches it.
87
+ """
88
+ src = """\
89
+ package main
90
+
91
+ // Deliberately unconventional opening.
92
+ func Work() {}
93
+ """
94
+ return "go", src, "Work", "Deliberately unconventional opening"
95
+
96
+
97
+ @case(tags=["undocumented", "no_docs"])
98
+ def case_go_fn_without_doc() -> DocstringCase:
99
+ """Undocumented function."""
100
+ src = """\
101
+ package main
102
+
103
+ func bare() {}
104
+ """
105
+ return "go", src, "bare", "PHANTOM_DOC_TEXT_SHOULD_NEVER_APPEAR"
106
+
107
+
108
+ @case(tags=["undocumented", "boundary_not_attached"])
109
+ def case_go_doc_detached_by_blank_line() -> DocstringCase:
110
+ """Blank line breaks attachment — the Go style guide
111
+ explicitly forbids a blank line between a doc comment and
112
+ its symbol.
113
+ """
114
+ src = """\
115
+ package main
116
+
117
+ // Orphan.
118
+
119
+ func Later() {}
120
+ """
121
+ return "go", src, "Later", "Orphan"
122
+
123
+
124
+ @case(tags=["undocumented", "invalid"])
125
+ def case_go_doc_comment_above_previous_function_not_attached() -> DocstringCase:
126
+ """Comment between two `func`s belongs to the second one,
127
+ not the first. The walk starts from the symbol and goes
128
+ *backwards*, so the comment correctly attaches to `Second`.
129
+ We probe the chunk for the *first* function to confirm it
130
+ does not steal the comment.
131
+ """
132
+ src = """\
133
+ package main
134
+
135
+ func First() {}
136
+
137
+ // Doc for Second.
138
+ func Second() {}
139
+ """
140
+ return "go", src, "First", "Doc for Second"
@@ -0,0 +1,294 @@
1
+ """Go extraction test cases."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pytest
6
+ from pytest_cases import case
7
+
8
+ type SymbolCase = tuple[str, str, list[tuple[str, str, str]]]
9
+ type ImportCase = tuple[str, str, dict[str, str]]
10
+ type MultiImportCase = tuple[str, str, int, list[dict[str, str]]]
11
+ type MixedCase = tuple[str, str, set[str], list[tuple[str, str]]]
12
+
13
+ _xfail_nested = pytest.mark.xfail(
14
+ reason="nested/chained destructuring unsupported — no query-only recursion",
15
+ strict=True,
16
+ )
17
+
18
+
19
+ @case(tags=["symbol"])
20
+ def case_go_function() -> SymbolCase:
21
+ """func hello() {}."""
22
+ src = """\
23
+ package main
24
+
25
+ func hello() {}
26
+ """
27
+ return "go", src, [("function", "hello", "")]
28
+
29
+
30
+ @case(tags=["symbol"])
31
+ def case_go_function_params() -> SymbolCase:
32
+ """func add(a int, b int) int."""
33
+ src = """\
34
+ package main
35
+
36
+ func add(a int, b int) int { return a + b }
37
+ """
38
+ return "go", src, [("function", "add", "")]
39
+
40
+
41
+ @case(tags=["symbol"])
42
+ def case_go_multiple_functions() -> SymbolCase:
43
+ """Multiple functions."""
44
+ src = """\
45
+ package main
46
+
47
+ func foo() {}
48
+ func bar() {}
49
+ func baz() {}
50
+ """
51
+ return (
52
+ "go",
53
+ src,
54
+ [
55
+ ("function", "foo", ""),
56
+ ("function", "bar", ""),
57
+ ("function", "baz", ""),
58
+ ],
59
+ )
60
+
61
+
62
+ @case(tags=["symbol"])
63
+ def case_go_method() -> SymbolCase:
64
+ """Value receiver method."""
65
+ src = """\
66
+ package main
67
+
68
+ type User struct{}
69
+
70
+ func (u User) Name() string { return u.name }
71
+ """
72
+ return "go", src, [("method", "Name", "User")]
73
+
74
+
75
+ @case(tags=["symbol"])
76
+ def case_go_pointer_method() -> SymbolCase:
77
+ """Pointer receiver method."""
78
+ src = """\
79
+ package main
80
+
81
+ type Svc struct{}
82
+
83
+ func (s *Svc) Start() {}
84
+ """
85
+ return "go", src, [("method", "Start", "Svc")]
86
+
87
+
88
+ @case(tags=["symbol"])
89
+ def case_go_struct() -> SymbolCase:
90
+ """type User struct."""
91
+ src = """\
92
+ package main
93
+
94
+ type User struct {
95
+ Name string
96
+ }
97
+ """
98
+ return "go", src, [("class", "User", "")]
99
+
100
+
101
+ @case(tags=["symbol"])
102
+ def case_go_interface() -> SymbolCase:
103
+ """type Reader interface."""
104
+ src = """\
105
+ package main
106
+
107
+ type Reader interface {
108
+ Read(p []byte) (int, error)
109
+ }
110
+ """
111
+ return "go", src, [("class", "Reader", "")]
112
+
113
+
114
+ @case(tags=["symbol"])
115
+ def case_go_type_alias() -> SymbolCase:
116
+ """type ID string."""
117
+ src = """\
118
+ package main
119
+
120
+ type ID string
121
+ """
122
+ return "go", src, [("class", "ID", "")]
123
+
124
+
125
+ @case(tags=["symbol"])
126
+ def case_go_multiple_types() -> SymbolCase:
127
+ """Multiple type declarations."""
128
+ src = """\
129
+ package main
130
+
131
+ type Foo struct{}
132
+ type Bar struct{}
133
+ """
134
+ return "go", src, [("class", "Foo", ""), ("class", "Bar", "")]
135
+
136
+
137
+ @case(tags=["import"])
138
+ def case_go_import_single() -> ImportCase:
139
+ """import "fmt"."""
140
+ src = """\
141
+ package main
142
+ import "fmt"
143
+ """
144
+ return "go", src, {"module": "fmt"}
145
+
146
+
147
+ @case(tags=["import"])
148
+ def case_go_import_nested() -> ImportCase:
149
+ """import "os/exec"."""
150
+ src = """\
151
+ package main
152
+ import "os/exec"
153
+ """
154
+ return "go", src, {"module": "os/exec"}
155
+
156
+
157
+ @case(tags=["import"])
158
+ def case_go_import_url() -> ImportCase:
159
+ """import "github.com/user/repo"."""
160
+ src = """\
161
+ package main
162
+ import "github.com/user/repo"
163
+ """
164
+ return "go", src, {"module": "github.com/user/repo"}
165
+
166
+
167
+ @case(tags=["import"])
168
+ def case_go_import_aliased() -> ImportCase:
169
+ """import f "fmt" — alias ignored, module captured."""
170
+ src = """\
171
+ package main
172
+ import f "fmt"
173
+ """
174
+ return "go", src, {"module": "fmt"}
175
+
176
+
177
+ @case(tags=["multi_import"])
178
+ def case_go_import_grouped() -> MultiImportCase:
179
+ """import ("fmt" "os") — separate chunks per spec."""
180
+ src = """\
181
+ package main
182
+ import (
183
+ "fmt"
184
+ "os"
185
+ )
186
+ """
187
+ return (
188
+ "go",
189
+ src,
190
+ 2,
191
+ [{"module": "fmt"}, {"module": "os"}],
192
+ )
193
+
194
+
195
+ @case(tags=["multi_import"])
196
+ def case_go_import_grouped_paths() -> MultiImportCase:
197
+ """Grouped import with nested paths — separate chunks."""
198
+ src = """\
199
+ package main
200
+ import (
201
+ "fmt"
202
+ "os/exec"
203
+ "net/http"
204
+ )
205
+ """
206
+ return (
207
+ "go",
208
+ src,
209
+ 3,
210
+ [{"module": "fmt"}, {"module": "os/exec"}, {"module": "net/http"}],
211
+ )
212
+
213
+
214
+ @case(tags=["import"])
215
+ def case_go_import_grouped_single() -> ImportCase:
216
+ """Grouped import with one item."""
217
+ src = """\
218
+ package main
219
+ import (
220
+ "fmt"
221
+ )
222
+ """
223
+ return "go", src, {"module": "fmt"}
224
+
225
+
226
+ @case(tags=["mixed"])
227
+ def case_go_full_module() -> MixedCase:
228
+ """Realistic Go module with godoc-style comments.
229
+
230
+ Expected-kinds tuple unchanged; content assertions are in
231
+ `test_docstrings.py`.
232
+ """
233
+ src = """\
234
+ package main
235
+
236
+ import (
237
+ "fmt"
238
+ "os"
239
+ )
240
+
241
+ // Config is the runtime configuration for the service.
242
+ type Config struct {
243
+ Name string
244
+ }
245
+
246
+ // String returns a human-readable summary of the Config.
247
+ func (c Config) String() string { return c.Name }
248
+
249
+ // main is the entry point for the service.
250
+ func main() {
251
+ fmt.Println("hello")
252
+ }
253
+ """
254
+ return "go", src, {"import", "class", "method", "function"}, [("String", "Config")]
255
+
256
+
257
+ @case(tags=["symbol"])
258
+ def case_go_package_var() -> SymbolCase:
259
+ """Package-level var."""
260
+ return "go", "package main\nvar MaxSize = 100\n", [("variable", "MaxSize", "")]
261
+
262
+
263
+ @case(tags=["symbol"])
264
+ def case_go_package_const() -> SymbolCase:
265
+ """Package-level const."""
266
+ return "go", "package main\nconst Timeout = 30\n", [("variable", "Timeout", "")]
267
+
268
+
269
+ @case(tags=["symbol"])
270
+ def case_go_grouped_var() -> SymbolCase:
271
+ """Go grouped var block."""
272
+ src = """\
273
+ package m
274
+
275
+ var (
276
+ X = 1
277
+ Y = 2
278
+ )
279
+ """
280
+ return "go", src, [("variable", "X", ""), ("variable", "Y", "")]
281
+
282
+
283
+ @case(tags=["symbol"])
284
+ def case_go_grouped_const() -> SymbolCase:
285
+ """Go grouped const block (already supported — regression guard)."""
286
+ src = """\
287
+ package m
288
+
289
+ const (
290
+ A = 1
291
+ B = 2
292
+ )
293
+ """
294
+ return "go", src, [("variable", "A", ""), ("variable", "B", "")]
@@ -0,0 +1,43 @@
1
+ // Package greet formats greetings for named recipients.
2
+ //
3
+ // The Go plugin extracts functions, methods (functions with a
4
+ // receiver), type declarations (struct, interface, and alias, as
5
+ // classes), const/var declarations (as variables), and imports.
6
+ package greet
7
+
8
+ import (
9
+ "fmt"
10
+
11
+ "greeter/util"
12
+ )
13
+
14
+ // DefaultGreeting is the fallback prefix.
15
+ const DefaultGreeting = "Hello"
16
+
17
+ var fallbackLocale = "en" // trailing comment: its own chunk, not folded
18
+
19
+ // Temperature is an alias for the underlying float type.
20
+ type Temperature = float64
21
+
22
+ // Greeter holds a greeting prefix.
23
+ type Greeter struct {
24
+ Prefix string
25
+ }
26
+
27
+ // Formatter renders a greeting for a name.
28
+ type Formatter interface {
29
+ Format(name string) string
30
+ }
31
+
32
+ // Standalone note, separated by blank lines from any definition.
33
+ // Second line of the same block.
34
+
35
+ // Greet greets a single recipient.
36
+ func (g Greeter) Greet(name string) string {
37
+ return fmt.Sprintf("%s, %s", g.Prefix, util.Trim(name))
38
+ }
39
+
40
+ // FormatGreeting formats a greeting via a Greeter.
41
+ func FormatGreeting(g Greeter, name string) string {
42
+ return g.Greet(name)
43
+ }
@@ -0,0 +1,9 @@
1
+ // Package util holds greeting helpers.
2
+ package util
3
+
4
+ import "strings"
5
+
6
+ // Trim removes surrounding whitespace from a name.
7
+ func Trim(name string) string {
8
+ return strings.TrimSpace(name)
9
+ }
@@ -0,0 +1,45 @@
1
+ """Go doc-comment extraction (Go 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 doc comment folds into its symbol: it appears in the chunk
40
+ content and the chunk starts on the comment's line, above the definition."""
41
+ chunk = next(
42
+ c for c in extract_file(FileEntry("input", "sha1", source.encode()), lang) if c.name == name
43
+ )
44
+ assert snippet in chunk.content
45
+ assert chunk.content.lstrip().startswith(("//", "/*"))
@@ -0,0 +1,57 @@
1
+ """Go 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,74 @@
1
+ """go sample extraction: the `samples/go/` 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" / "go"
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 "go"
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.METHOD,
52
+ ChunkKind.CLASS,
53
+ ChunkKind.VARIABLE,
54
+ ChunkKind.IMPORT,
55
+ ChunkKind.COMMENT,
56
+ } <= kinds
57
+
58
+
59
+ def test_parses_cleanly(project: list[tuple[str, str]]) -> None:
60
+ manager = get_manager()
61
+ for path, text in project:
62
+ grammar = manager.grammar(manager.detect_language(path) or "go")
63
+ assert grammar is not None
64
+ assert not Parser(grammar).parse(text.encode()).root_node.has_error, path
65
+
66
+
67
+ def test_extraction_matches_snapshot(chunks: list[Chunk], snapshot_json: SnapshotAssertion) -> None:
68
+ assert chunks == snapshot_json
69
+
70
+
71
+ def test_edges_match_snapshot(
72
+ chunks: list[Chunk], edges: list[Edge], snapshot_json: SnapshotAssertion
73
+ ) -> None:
74
+ assert render_edges(edges, chunks) == snapshot_json
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: rbtr-lang-go
3
+ Version: 2026.7.0.dev0
4
+ Summary: rbtr — Go language plugin
5
+ License-Expression: MIT
6
+ Requires-Dist: rbtr==2026.7.0.dev0
7
+ Requires-Dist: tree-sitter-go
8
+ Requires-Python: >=3.13
@@ -0,0 +1,18 @@
1
+ rbtr_lang_go/__init__.py,sha256=ESnwM06MFOjJEdiK8MbCsQGStwLJ-2n0AIgzYpsuZkg,34
2
+ rbtr_lang_go/go.scm,sha256=t2T5WwXqMNkGa0TT9PIT9ue_2nTO-TubZdKobz90v5w,1271
3
+ rbtr_lang_go/plugin.py,sha256=zKsesdh0CLCRdr68dKbbb_5xjtzu5zQjzBvTS-yQpIw,1342
4
+ rbtr_lang_go/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ rbtr_lang_go/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ rbtr_lang_go/tests/__snapshots__/test_samples/test_edges_match_snapshot.json,sha256=nBRO23R3ddiNmQUm5ndA5lSDAdOJH0iAQWPErqpe4As,67
7
+ rbtr_lang_go/tests/__snapshots__/test_samples/test_extraction_matches_snapshot.json,sha256=6Iy2ie47Ety7dICpKLXhh5bOA3DBXuSTU0rMCngRZyg,7182
8
+ rbtr_lang_go/tests/cases_docstrings.py,sha256=9cV3w9AxmMm9ZDaO3u1Bv-AHeXHAgnHL7IHe-U9fq9A,3453
9
+ rbtr_lang_go/tests/cases_extraction.py,sha256=hprGkPynyITK8U2VY_bdk6E6nA6HBEcF8UWK_9J_o7o,5810
10
+ rbtr_lang_go/tests/samples/go/go.go,sha256=_PhwkrAMIaFZfXO-4WkEr0b7thT_GQ1s1HAP2T70vtE,1090
11
+ rbtr_lang_go/tests/samples/go/greeter/util.go,sha256=6JsGe7imJr6rcCj4jfTJf9uDpbTGD7l0sYtI4BGvYlU,190
12
+ rbtr_lang_go/tests/test_docstrings.py,sha256=HLh5Rv-LRO_Fjt04yJ1jijwjDpnSJxJ2KqrJHWWr8VY,1891
13
+ rbtr_lang_go/tests/test_extraction.py,sha256=5CmjbgGegt-UWCvtTO_6cIJx39BuWawhJHj-SAmTn9s,2652
14
+ rbtr_lang_go/tests/test_samples.py,sha256=JdFj3Zyq6NnbPukt76KOI7fiWr-WmMQbCIy9BqGO94o,2290
15
+ rbtr_lang_go-2026.7.0.dev0.dist-info/WHEEL,sha256=CoDSoyhtC_eO_tlxRYzsTraPv1fPJRXFx91k6ISeAvA,81
16
+ rbtr_lang_go-2026.7.0.dev0.dist-info/entry_points.txt,sha256=7KT_1_hCbZVpXXkfDXPjHoop0XPZK3GUCGqczFOrWGw,46
17
+ rbtr_lang_go-2026.7.0.dev0.dist-info/METADATA,sha256=c_RgDm3iQeqSO9C6BOo6ABB7N-BkZ4ul3oFElZkegL4,214
18
+ rbtr_lang_go-2026.7.0.dev0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.11.28
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [rbtr.languages]
2
+ go = rbtr_lang_go.plugin:go
3
+