Skip to main content

code_moniker_core/lang/go/
mod.rs

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