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}