1use tree_sitter::{Language, Parser, Tree};
2
3use crate::core::code_graph::CodeGraph;
4use crate::core::moniker::Moniker;
5use crate::core::shape::Shape;
6
7use crate::lang::KindSpec;
8
9pub mod build;
10mod kinds;
11mod sdk_pipeline;
12
13#[derive(Clone, Debug, Default)]
14pub struct Presets {}
15
16pub fn parse(source: &str) -> Tree {
17 let mut parser = Parser::new();
18 let language: Language = tree_sitter_go::LANGUAGE.into();
19 parser.set_language(&language).unwrap_or_else(|err| {
20 panic!("failed to load tree-sitter Go grammar: {err}");
21 });
22 parser.parse(source, None).unwrap_or_else(|| {
23 panic!("tree-sitter parse returned None on a non-cancelled call");
24 })
25}
26
27pub fn extract(
28 uri: &str,
29 source: &str,
30 anchor: &Moniker,
31 deep: bool,
32 presets: &Presets,
33) -> CodeGraph {
34 sdk_pipeline::extract(uri, source, anchor, deep, presets)
35}
36
37pub struct Lang;
38
39const DEF_KINDS: &[&str] = &[
40 "type",
41 "struct",
42 "interface",
43 "func",
44 "method",
45 "field",
46 "var",
47 "const",
48];
49
50const DEF_KIND_SPECS: &[KindSpec] = &[
51 KindSpec::new("struct", Shape::Type, 20, "struct"),
52 KindSpec::new("interface", Shape::Type, 21, "interface"),
53 KindSpec::new("type", Shape::Type, 22, "type"),
54 KindSpec::new("field", Shape::Value, 30, "field"),
55 KindSpec::new("func", Shape::Callable, 40, "func"),
56 KindSpec::new("method", Shape::Callable, 41, "method"),
57 KindSpec::new("const", Shape::Value, 60, "const"),
58 KindSpec::new("var", Shape::Value, 61, "var"),
59];
60
61impl crate::lang::LangExtractor for Lang {
62 type Presets = Presets;
63 const LANG_TAG: &'static str = "go";
64 const ALLOWED_KINDS: &'static [&'static str] = DEF_KINDS;
65 const KIND_SPECS: &'static [KindSpec] = DEF_KIND_SPECS;
66 const ALLOWED_VISIBILITIES: &'static [&'static str] = &["public", "module"];
67
68 fn extract(
69 uri: &str,
70 source: &str,
71 anchor: &Moniker,
72 deep: bool,
73 presets: &Self::Presets,
74 ) -> CodeGraph {
75 extract(uri, source, anchor, deep, presets)
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::core::moniker::MonikerBuilder;
83 use crate::lang::assert_conformance;
84
85 fn make_anchor() -> Moniker {
86 MonikerBuilder::new().project(b"app").build()
87 }
88
89 fn extract_default(uri: &str, source: &str, anchor: &Moniker, deep: bool) -> CodeGraph {
90 let g = extract(uri, source, anchor, deep, &Presets::default());
91 assert_conformance::<super::Lang>(&g, anchor);
92 g
93 }
94
95 #[test]
96 fn parse_empty_returns_source_file() {
97 let tree = parse("");
98 assert_eq!(tree.root_node().kind(), "source_file");
99 }
100
101 #[test]
102 fn extract_module_uses_path_segments() {
103 let g = extract_default("acme/util/text.go", "package text\n", &make_anchor(), false);
104 let expected = MonikerBuilder::new()
105 .project(b"app")
106 .segment(b"lang", b"go")
107 .segment(b"package", b"acme")
108 .segment(b"package", b"util")
109 .segment(b"module", b"text")
110 .build();
111 assert_eq!(g.root(), &expected);
112 }
113
114 #[test]
115 fn extract_method_when_type_declared_after_method() {
116 let src = "package foo\nfunc (r *Foo) Bar() {}\ntype Foo struct{}\n";
117 let g = extract_default("foo.go", src, &make_anchor(), false);
118 let bar = MonikerBuilder::new()
119 .project(b"app")
120 .segment(b"lang", b"go")
121 .segment(b"module", b"foo")
122 .segment(b"struct", b"Foo")
123 .segment(b"method", b"Bar()")
124 .build();
125 assert!(
126 g.contains(&bar),
127 "method emitted before its type declaration must still be reparented; defs: {:?}",
128 g.def_monikers()
129 );
130 let bar_def = g.defs().find(|d| d.moniker == bar).expect("method def");
131 let defs: Vec<_> = g.defs().collect();
132 let parent = bar_def
133 .parent
134 .and_then(|idx| defs.get(idx).copied())
135 .expect("method parent");
136 let foo = MonikerBuilder::new()
137 .project(b"app")
138 .segment(b"lang", b"go")
139 .segment(b"module", b"foo")
140 .segment(b"struct", b"Foo")
141 .build();
142 assert_eq!(
143 parent.moniker, foo,
144 "method parent metadata must point at receiver type"
145 );
146 }
147
148 #[test]
149 fn extract_simple_call_to_unresolved_callee_uses_name_only() {
150 let src = "package foo\nfunc Run() { Helper(1, 2) }\n";
151 let g = extract_default("foo.go", src, &make_anchor(), false);
152 let r = g
153 .refs()
154 .find(|r| {
155 r.kind == b"calls"
156 && r.target.as_view().segments().last().unwrap().name == b"Helper"
157 })
158 .expect("calls Helper (name-only, no parens)");
159 assert_eq!(r.confidence, b"name_match".to_vec());
160 }
161
162 #[test]
163 fn extract_composite_literal_unresolved_type_marks_name_match() {
164 let src = "package foo\nfunc Run() { _ = Bar{} }\n";
165 let g = extract_default("foo.go", src, &make_anchor(), false);
166 let r = g
167 .refs()
168 .find(|r| r.kind == b"instantiates")
169 .expect("instantiates ref");
170 assert_eq!(r.confidence, b"name_match".to_vec());
171 }
172
173 #[test]
174 fn extract_shallow_skips_param_and_local_defs() {
175 let src = "package foo\nfunc Run(x int) { y := 1; _ = y }\n";
176 let g = extract_default("foo.go", src, &make_anchor(), false);
177 assert!(
178 g.defs().all(|d| d.kind != b"param" && d.kind != b"local"),
179 "shallow extraction must not emit param/local defs"
180 );
181 }
182
183 #[test]
184 fn extract_deep_emits_param_defs_under_function() {
185 let src = "package foo\nfunc Run(a int, b string) {}\n";
186 let g = extract_default("foo.go", src, &make_anchor(), true);
187 let pa = MonikerBuilder::new()
188 .project(b"app")
189 .segment(b"lang", b"go")
190 .segment(b"module", b"foo")
191 .segment(b"func", b"Run(a:int,b:string)")
192 .segment(b"param", b"a")
193 .build();
194 let pb = MonikerBuilder::new()
195 .project(b"app")
196 .segment(b"lang", b"go")
197 .segment(b"module", b"foo")
198 .segment(b"func", b"Run(a:int,b:string)")
199 .segment(b"param", b"b")
200 .build();
201 assert!(g.contains(&pa));
202 assert!(g.contains(&pb));
203 }
204
205 #[test]
206 fn extract_deep_emits_receiver_param_for_method() {
207 let src = "package foo\ntype Foo struct{}\nfunc (r *Foo) Bar(x int) {}\n";
208 let g = extract_default("foo.go", src, &make_anchor(), true);
209 let recv = MonikerBuilder::new()
210 .project(b"app")
211 .segment(b"lang", b"go")
212 .segment(b"module", b"foo")
213 .segment(b"struct", b"Foo")
214 .segment(b"method", b"Bar(x:int)")
215 .segment(b"param", b"r")
216 .build();
217 assert!(g.contains(&recv));
218 }
219
220 #[test]
221 fn extract_deep_skips_blank_param() {
222 let src = "package foo\nfunc Run(_ int, b string) {}\n";
223 let g = extract_default("foo.go", src, &make_anchor(), true);
224 let params: Vec<&[u8]> = g
225 .defs()
226 .filter(|d| d.kind == b"param")
227 .map(|d| d.moniker.as_view().segments().last().unwrap().name)
228 .collect();
229 assert_eq!(params, vec![&b"b"[..]]);
230 }
231
232 #[test]
233 fn extract_deep_emits_local_def_for_short_var() {
234 let src = "package foo\nfunc Run() { x := 1; _ = x }\n";
235 let g = extract_default("foo.go", src, &make_anchor(), true);
236 let lx = MonikerBuilder::new()
237 .project(b"app")
238 .segment(b"lang", b"go")
239 .segment(b"module", b"foo")
240 .segment(b"func", b"Run()")
241 .segment(b"local", b"x")
242 .build();
243 assert!(g.contains(&lx));
244 }
245
246 #[test]
247 fn extract_deep_emits_local_defs_for_multi_assign() {
248 let src = "package foo\nfunc Run() { x, y := 1, 2; _, _ = x, y }\n";
249 let g = extract_default("foo.go", src, &make_anchor(), true);
250 let names: Vec<&[u8]> = g
251 .defs()
252 .filter(|d| d.kind == b"local")
253 .map(|d| d.moniker.as_view().segments().last().unwrap().name)
254 .collect();
255 assert!(names.contains(&&b"x"[..]));
256 assert!(names.contains(&&b"y"[..]));
257 }
258
259 #[test]
260 fn extract_deep_emits_local_def_for_var_declaration() {
261 let src = "package foo\nfunc Run() { var z int = 5; _ = z }\n";
262 let g = extract_default("foo.go", src, &make_anchor(), true);
263 let lz = MonikerBuilder::new()
264 .project(b"app")
265 .segment(b"lang", b"go")
266 .segment(b"module", b"foo")
267 .segment(b"func", b"Run()")
268 .segment(b"local", b"z")
269 .build();
270 assert!(g.contains(&lz));
271 }
272
273 #[test]
274 fn extract_deep_emits_local_defs_for_range_vars() {
275 let src =
276 "package foo\nfunc Run(m map[string]int) { for k, v := range m { _, _ = k, v } }\n";
277 let g = extract_default("foo.go", src, &make_anchor(), true);
278 let names: Vec<&[u8]> = g
279 .defs()
280 .filter(|d| d.kind == b"local")
281 .map(|d| d.moniker.as_view().segments().last().unwrap().name)
282 .collect();
283 assert!(names.contains(&&b"k"[..]));
284 assert!(names.contains(&&b"v"[..]));
285 }
286
287 #[test]
288 fn extract_top_level_var_does_not_pollute_locals() {
289 let src = "package foo\nvar GlobalCount int\nfunc Run() { GlobalCount = 1 }\n";
290 let g = extract_default("foo.go", src, &make_anchor(), false);
291 let local_names: Vec<&[u8]> = g
292 .defs()
293 .filter(|d| d.kind == b"local")
294 .map(|d| d.moniker.as_view().segments().last().unwrap().name)
295 .collect();
296 assert!(
297 local_names.is_empty(),
298 "a package-level var must not be emitted as a local. found locals: {:?}",
299 local_names
300 );
301 let vars: Vec<&[u8]> = g
302 .defs()
303 .filter(|d| d.kind == b"var")
304 .map(|d| d.moniker.as_view().segments().last().unwrap().name)
305 .collect();
306 assert_eq!(vars, vec![&b"GlobalCount"[..]]);
307 }
308
309 #[test]
310 fn extract_deep_skips_blank_in_short_var() {
311 let src = "package foo\nfunc Run() { _, y := 1, 2; _ = y }\n";
312 let g = extract_default("foo.go", src, &make_anchor(), true);
313 let names: Vec<&[u8]> = g
314 .defs()
315 .filter(|d| d.kind == b"local")
316 .map(|d| d.moniker.as_view().segments().last().unwrap().name)
317 .collect();
318 assert_eq!(names, vec![&b"y"[..]]);
319 }
320}