Skip to main content

code_moniker_core/lang/go/
mod.rs

1use std::cell::RefCell;
2use std::collections::HashMap;
3
4use tree_sitter::{Language, Parser, Tree};
5
6use crate::core::code_graph::CodeGraph;
7use crate::core::moniker::Moniker;
8
9use crate::lang::canonical_walker::CanonicalWalker;
10
11pub mod build;
12mod canonicalize;
13mod kinds;
14mod strategy;
15
16use canonicalize::compute_module_moniker;
17use strategy::{ImportEntry, Strategy, collect_callable_table, collect_type_table};
18
19#[derive(Clone, Debug, Default)]
20pub struct Presets {}
21
22pub fn parse(source: &str) -> Tree {
23	let mut parser = Parser::new();
24	let language: Language = tree_sitter_go::LANGUAGE.into();
25	parser
26		.set_language(&language)
27		.expect("failed to load tree-sitter Go grammar");
28	parser
29		.parse(source, None)
30		.expect("tree-sitter parse returned None on a non-cancelled call")
31}
32
33pub fn extract(
34	uri: &str,
35	source: &str,
36	anchor: &Moniker,
37	deep: bool,
38	_presets: &Presets,
39) -> CodeGraph {
40	let tree = parse(source);
41	let module = compute_module_moniker(anchor, uri);
42	let (def_cap, ref_cap) = CodeGraph::capacity_for_source(source.len());
43	let mut graph = CodeGraph::with_capacity(module.clone(), kinds::MODULE, def_cap, ref_cap);
44	let mut type_table: HashMap<&[u8], Moniker> = HashMap::new();
45	collect_type_table(
46		tree.root_node(),
47		source.as_bytes(),
48		&module,
49		&mut graph,
50		&mut type_table,
51	);
52	let mut callable_table: HashMap<(Moniker, Vec<u8>), Vec<u8>> = HashMap::new();
53	collect_callable_table(
54		tree.root_node(),
55		source.as_bytes(),
56		&module,
57		&type_table,
58		&mut callable_table,
59	);
60	let strat = Strategy {
61		module: module.clone(),
62		source_bytes: source.as_bytes(),
63		deep,
64		imports: RefCell::new(HashMap::<Vec<u8>, ImportEntry>::new()),
65		local_scope: RefCell::new(Vec::new()),
66		type_table,
67		callable_table,
68	};
69	let walker = CanonicalWalker::new(&strat, source.as_bytes());
70	walker.walk(tree.root_node(), &module, &mut graph);
71	graph
72}
73
74pub struct Lang;
75
76impl crate::lang::LangExtractor for Lang {
77	type Presets = Presets;
78	const LANG_TAG: &'static str = "go";
79	const ALLOWED_KINDS: &'static [&'static str] = &[
80		"type",
81		"struct",
82		"interface",
83		"func",
84		"method",
85		"var",
86		"const",
87	];
88	const ALLOWED_VISIBILITIES: &'static [&'static str] = &["public", "module"];
89
90	fn extract(
91		uri: &str,
92		source: &str,
93		anchor: &Moniker,
94		deep: bool,
95		presets: &Self::Presets,
96	) -> CodeGraph {
97		extract(uri, source, anchor, deep, presets)
98	}
99}
100
101#[cfg(test)]
102mod tests {
103	use super::*;
104	use crate::core::moniker::MonikerBuilder;
105	use crate::lang::assert_conformance;
106
107	fn make_anchor() -> Moniker {
108		MonikerBuilder::new().project(b"app").build()
109	}
110
111	fn extract_default(uri: &str, source: &str, anchor: &Moniker, deep: bool) -> CodeGraph {
112		let g = extract(uri, source, anchor, deep, &Presets::default());
113		assert_conformance::<super::Lang>(&g, anchor);
114		g
115	}
116
117	#[test]
118	fn parse_empty_returns_source_file() {
119		let tree = parse("");
120		assert_eq!(tree.root_node().kind(), "source_file");
121	}
122
123	#[test]
124	fn extract_emits_comment_def_per_comment_node() {
125		let src = "package text\n// a\n/* b */\nfunc Foo() {}\n";
126		let g = extract_default("text.go", src, &make_anchor(), false);
127		let n = g.defs().filter(|d| d.kind == b"comment").count();
128		assert_eq!(n, 2);
129	}
130
131	#[test]
132	fn extract_module_uses_path_segments() {
133		let g = extract_default("acme/util/text.go", "package text\n", &make_anchor(), false);
134		let expected = MonikerBuilder::new()
135			.project(b"app")
136			.segment(b"lang", b"go")
137			.segment(b"package", b"acme")
138			.segment(b"package", b"util")
139			.segment(b"module", b"text")
140			.build();
141		assert_eq!(g.root(), &expected);
142	}
143
144	#[test]
145	fn extract_module_root_is_filename_only() {
146		let g = extract_default("foo.go", "package foo\n", &make_anchor(), false);
147		let expected = MonikerBuilder::new()
148			.project(b"app")
149			.segment(b"lang", b"go")
150			.segment(b"module", b"foo")
151			.build();
152		assert_eq!(g.root(), &expected);
153	}
154
155	#[test]
156	fn extract_function_with_typed_params_emits_full_signature() {
157		let src = "package foo\nfunc Add(a int, b int) int { return a + b }\n";
158		let g = extract_default("foo.go", src, &make_anchor(), false);
159		let f = g.defs().find(|d| d.kind == b"func").expect("function def");
160		let last = f.moniker.as_view().segments().last().unwrap();
161		assert_eq!(last.kind, b"func");
162		assert_eq!(last.name, b"Add(a:int,b:int)");
163		assert_eq!(f.signature, b"a:int,b:int".to_vec());
164	}
165
166	#[test]
167	fn extract_function_grouped_param_names_repeat_type() {
168		let src = "package foo\nfunc Add(a, b int) int { return a + b }\n";
169		let g = extract_default("foo.go", src, &make_anchor(), false);
170		let f = g.defs().find(|d| d.kind == b"func").expect("function def");
171		assert_eq!(
172			f.moniker.as_view().segments().last().unwrap().name,
173			b"Add(a:int,b:int)",
174			"`a, b int` must expand to two named slots sharing the type"
175		);
176	}
177
178	#[test]
179	fn extract_function_no_params_emits_empty_parens() {
180		let src = "package foo\nfunc Boot() {}\n";
181		let g = extract_default("foo.go", src, &make_anchor(), false);
182		let f = g.defs().find(|d| d.kind == b"func").expect("function def");
183		assert_eq!(
184			f.moniker.as_view().segments().last().unwrap().name,
185			b"Boot()"
186		);
187	}
188
189	#[test]
190	fn extract_function_capitalized_name_is_public() {
191		let src = "package foo\nfunc Hello() {}\n";
192		let g = extract_default("foo.go", src, &make_anchor(), false);
193		let f = g.defs().find(|d| d.kind == b"func").expect("function def");
194		assert_eq!(f.visibility, b"public".to_vec());
195	}
196
197	#[test]
198	fn extract_function_lowercase_name_is_module() {
199		let src = "package foo\nfunc helper() {}\n";
200		let g = extract_default("foo.go", src, &make_anchor(), false);
201		let f = g.defs().find(|d| d.kind == b"func").expect("function def");
202		assert_eq!(f.visibility, b"module".to_vec());
203	}
204
205	#[test]
206	fn extract_variadic_function_emits_ellipsis_slot() {
207		let src = "package foo\nfunc Printf(args ...int) {}\n";
208		let g = extract_default("foo.go", src, &make_anchor(), false);
209		let f = g.defs().find(|d| d.kind == b"func").expect("function def");
210		assert_eq!(
211			f.moniker.as_view().segments().last().unwrap().name,
212			b"Printf(...)"
213		);
214	}
215
216	#[test]
217	fn extract_struct_emits_struct_def() {
218		let src = "package foo\ntype Foo struct { X int }\n";
219		let g = extract_default("foo.go", src, &make_anchor(), false);
220		let foo = g.defs().find(|d| d.kind == b"struct").expect("struct def");
221		assert_eq!(
222			foo.moniker.as_view().segments().last().unwrap().name,
223			b"Foo"
224		);
225		assert_eq!(foo.visibility, b"public".to_vec());
226	}
227
228	#[test]
229	fn extract_interface_emits_interface_def() {
230		let src = "package foo\ntype Greeter interface { Hello() string }\n";
231		let g = extract_default("foo.go", src, &make_anchor(), false);
232		let i = g
233			.defs()
234			.find(|d| d.kind == b"interface")
235			.expect("interface def");
236		assert_eq!(
237			i.moniker.as_view().segments().last().unwrap().name,
238			b"Greeter"
239		);
240	}
241
242	#[test]
243	fn extract_defined_type_emits_type_alias_def() {
244		let src = "package foo\ntype UserID int\n";
245		let g = extract_default("foo.go", src, &make_anchor(), false);
246		let t = g
247			.defs()
248			.find(|d| d.kind == b"type")
249			.expect("type_alias def");
250		assert_eq!(
251			t.moniker.as_view().segments().last().unwrap().name,
252			b"UserID"
253		);
254	}
255
256	#[test]
257	fn extract_type_alias_with_equals_emits_type_alias_def() {
258		let src = "package foo\ntype UserID = int\n";
259		let g = extract_default("foo.go", src, &make_anchor(), false);
260		let t = g
261			.defs()
262			.find(|d| d.kind == b"type")
263			.expect("type_alias def");
264		assert_eq!(
265			t.moniker.as_view().segments().last().unwrap().name,
266			b"UserID"
267		);
268	}
269
270	#[test]
271	fn extract_grouped_type_decl_emits_each_def() {
272		let src = "package foo\ntype (\n\tFoo struct{}\n\tBar interface{}\n)\n";
273		let g = extract_default("foo.go", src, &make_anchor(), false);
274		assert!(g.defs().any(|d| d.kind == b"struct"
275			&& d.moniker.as_view().segments().last().unwrap().name == b"Foo"));
276		assert!(g.defs().any(|d| d.kind == b"interface"
277			&& d.moniker.as_view().segments().last().unwrap().name == b"Bar"));
278	}
279
280	#[test]
281	fn extract_unexported_struct_visibility_is_module() {
282		let src = "package foo\ntype internal struct{}\n";
283		let g = extract_default("foo.go", src, &make_anchor(), false);
284		let t = g.defs().find(|d| d.kind == b"struct").expect("struct def");
285		assert_eq!(t.visibility, b"module".to_vec());
286	}
287
288	#[test]
289	fn extract_method_reparents_to_receiver_type() {
290		let src = "package foo\ntype Foo struct{}\nfunc (r Foo) Bar(x int) int { return x }\n";
291		let g = extract_default("foo.go", src, &make_anchor(), false);
292		let bar = MonikerBuilder::new()
293			.project(b"app")
294			.segment(b"lang", b"go")
295			.segment(b"module", b"foo")
296			.segment(b"struct", b"Foo")
297			.segment(b"method", b"Bar(x:int)")
298			.build();
299		assert!(
300			g.contains(&bar),
301			"expected {bar:?}, defs: {:?}",
302			g.def_monikers()
303		);
304	}
305
306	#[test]
307	fn extract_method_with_pointer_receiver_strips_star() {
308		let src = "package foo\ntype Foo struct{}\nfunc (r *Foo) Bar() {}\n";
309		let g = extract_default("foo.go", src, &make_anchor(), false);
310		let bar = MonikerBuilder::new()
311			.project(b"app")
312			.segment(b"lang", b"go")
313			.segment(b"module", b"foo")
314			.segment(b"struct", b"Foo")
315			.segment(b"method", b"Bar()")
316			.build();
317		assert!(g.contains(&bar));
318	}
319
320	#[test]
321	fn extract_method_when_type_declared_after_method() {
322		let src = "package foo\nfunc (r *Foo) Bar() {}\ntype Foo struct{}\n";
323		let g = extract_default("foo.go", src, &make_anchor(), false);
324		let bar = MonikerBuilder::new()
325			.project(b"app")
326			.segment(b"lang", b"go")
327			.segment(b"module", b"foo")
328			.segment(b"struct", b"Foo")
329			.segment(b"method", b"Bar()")
330			.build();
331		assert!(
332			g.contains(&bar),
333			"method emitted before its type declaration must still be reparented; defs: {:?}",
334			g.def_monikers()
335		);
336	}
337
338	#[test]
339	fn extract_method_signature_excludes_receiver() {
340		let src =
341			"package foo\ntype Foo struct{}\nfunc (r Foo) Sum(a, b int) int { return a + b }\n";
342		let g = extract_default("foo.go", src, &make_anchor(), false);
343		let m = g.defs().find(|d| d.kind == b"method").expect("method def");
344		assert_eq!(
345			m.moniker.as_view().segments().last().unwrap().name,
346			b"Sum(a:int,b:int)"
347		);
348		assert_eq!(m.signature, b"a:int,b:int".to_vec());
349	}
350
351	#[test]
352	fn extract_method_capitalized_visibility_public() {
353		let src = "package foo\ntype Foo struct{}\nfunc (f Foo) Public() {}\n";
354		let g = extract_default("foo.go", src, &make_anchor(), false);
355		let m = g.defs().find(|d| d.kind == b"method").expect("method def");
356		assert_eq!(m.visibility, b"public".to_vec());
357	}
358
359	#[test]
360	fn extract_method_lowercase_visibility_module() {
361		let src = "package foo\ntype Foo struct{}\nfunc (f Foo) hidden() {}\n";
362		let g = extract_default("foo.go", src, &make_anchor(), false);
363		let m = g.defs().find(|d| d.kind == b"method").expect("method def");
364		assert_eq!(m.visibility, b"module".to_vec());
365	}
366
367	#[test]
368	fn extract_import_simple_emits_imports_module() {
369		let g = extract_default(
370			"foo.go",
371			"package foo\nimport \"fmt\"\n",
372			&make_anchor(),
373			false,
374		);
375		let r = g
376			.refs()
377			.find(|r| r.kind == b"imports_module")
378			.expect("imports_module ref");
379		let target = MonikerBuilder::new()
380			.project(b"app")
381			.segment(b"external_pkg", b"fmt")
382			.build();
383		assert_eq!(r.target, target);
384		assert_eq!(r.confidence, b"external".to_vec());
385		assert!(r.alias.is_empty());
386	}
387
388	#[test]
389	fn extract_import_path_with_slashes_uses_path_segments() {
390		let g = extract_default(
391			"foo.go",
392			"package foo\nimport \"net/http\"\n",
393			&make_anchor(),
394			false,
395		);
396		let r = g
397			.refs()
398			.find(|r| r.kind == b"imports_module")
399			.expect("imports_module ref");
400		let target = MonikerBuilder::new()
401			.project(b"app")
402			.segment(b"external_pkg", b"net")
403			.segment(b"path", b"http")
404			.build();
405		assert_eq!(r.target, target);
406		assert_eq!(r.confidence, b"external".to_vec());
407	}
408
409	#[test]
410	fn extract_import_third_party_marks_imported() {
411		let g = extract_default(
412			"foo.go",
413			"package foo\nimport \"github.com/gorilla/mux\"\n",
414			&make_anchor(),
415			false,
416		);
417		let r = g
418			.refs()
419			.find(|r| r.kind == b"imports_module")
420			.expect("imports_module ref");
421		assert_eq!(r.confidence, b"imported".to_vec());
422		let target = MonikerBuilder::new()
423			.project(b"app")
424			.segment(b"external_pkg", b"github.com")
425			.segment(b"path", b"gorilla")
426			.segment(b"path", b"mux")
427			.build();
428		assert_eq!(r.target, target);
429	}
430
431	#[test]
432	fn extract_import_alias_records_alias_attr() {
433		let g = extract_default(
434			"foo.go",
435			"package foo\nimport f \"fmt\"\n",
436			&make_anchor(),
437			false,
438		);
439		let r = g
440			.refs()
441			.find(|r| r.kind == b"imports_module")
442			.expect("imports_module ref");
443		assert_eq!(r.alias, b"f".to_vec());
444	}
445
446	#[test]
447	fn extract_import_dot_emits_dot_alias() {
448		let g = extract_default(
449			"foo.go",
450			"package foo\nimport . \"fmt\"\n",
451			&make_anchor(),
452			false,
453		);
454		let r = g
455			.refs()
456			.find(|r| r.kind == b"imports_module")
457			.expect("imports_module ref");
458		assert_eq!(r.alias, b".".to_vec());
459	}
460
461	#[test]
462	fn extract_import_blank_emits_underscore_alias() {
463		let g = extract_default(
464			"foo.go",
465			"package foo\nimport _ \"fmt\"\n",
466			&make_anchor(),
467			false,
468		);
469		let r = g
470			.refs()
471			.find(|r| r.kind == b"imports_module")
472			.expect("imports_module ref");
473		assert_eq!(r.alias, b"_".to_vec());
474	}
475
476	#[test]
477	fn extract_grouped_imports_emit_one_ref_per_spec() {
478		let src = "package foo\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"github.com/x/y\"\n)\n";
479		let g = extract_default("foo.go", src, &make_anchor(), false);
480		let count = g.refs().filter(|r| r.kind == b"imports_module").count();
481		assert_eq!(count, 3);
482	}
483
484	#[test]
485	fn extract_simple_call_to_unresolved_callee_uses_name_only() {
486		let src = "package foo\nfunc Run() { Helper(1, 2) }\n";
487		let g = extract_default("foo.go", src, &make_anchor(), false);
488		let r = g
489			.refs()
490			.find(|r| {
491				r.kind == b"calls"
492					&& r.target.as_view().segments().last().unwrap().name == b"Helper"
493			})
494			.expect("calls Helper (name-only, no parens)");
495		assert_eq!(r.confidence, b"name_match".to_vec());
496	}
497
498	#[test]
499	fn extract_simple_call_to_same_module_func_resolves_slots() {
500		let src = "package foo\nfunc Run() { Helper(1) }\nfunc Helper(n int) {}\n";
501		let g = extract_default("foo.go", src, &make_anchor(), false);
502		let r = g
503			.refs()
504			.find(|r| {
505				r.kind == b"calls"
506					&& r.target.as_view().segments().last().unwrap().name == b"Helper(n:int)"
507			})
508			.expect("calls Helper(n:int)");
509		assert_eq!(r.confidence, b"name_match".to_vec());
510	}
511
512	#[test]
513	fn extract_imported_call_uses_name_only_target() {
514		let src = "package foo\nimport \"net/http\"\nfunc Run() { http.Get(\"u\") }\n";
515		let g = extract_default("foo.go", src, &make_anchor(), false);
516		let r = g
517			.refs()
518			.find(|r| {
519				r.kind == b"calls" && r.target.as_view().segments().last().unwrap().name == b"Get"
520			})
521			.expect("calls http.Get");
522		assert_eq!(r.confidence, b"external".to_vec());
523		let segs: Vec<&[u8]> = r.target.as_view().segments().map(|s| s.kind).collect();
524		assert_eq!(segs, vec![&b"external_pkg"[..], &b"path"[..], &b"func"[..]]);
525	}
526
527	#[test]
528	fn extract_imported_simple_path_call_target() {
529		let src = "package foo\nimport \"fmt\"\nfunc Run() { fmt.Println(1) }\n";
530		let g = extract_default("foo.go", src, &make_anchor(), false);
531		let r = g
532			.refs()
533			.find(|r| {
534				r.kind == b"calls"
535					&& r.target.as_view().segments().last().unwrap().name == b"Println"
536			})
537			.expect("calls fmt.Println");
538		let names: Vec<&[u8]> = r.target.as_view().segments().map(|s| s.name).collect();
539		assert_eq!(names, vec![&b"fmt"[..], &b"Println"[..]]);
540	}
541
542	#[test]
543	fn extract_third_party_call_marks_imported() {
544		let src = "package foo\nimport \"github.com/x/mux\"\nfunc Run() { mux.New() }\n";
545		let g = extract_default("foo.go", src, &make_anchor(), false);
546		let r = g
547			.refs()
548			.find(|r| {
549				r.kind == b"calls" && r.target.as_view().segments().last().unwrap().name == b"New"
550			})
551			.expect("calls mux.New");
552		assert_eq!(r.confidence, b"imported".to_vec());
553	}
554
555	#[test]
556	fn extract_method_call_carries_receiver_hint_identifier() {
557		let src = "package foo\nfunc Run(obj T) { obj.Bar() }\n";
558		let g = extract_default("foo.go", src, &make_anchor(), false);
559		let r = g
560			.refs()
561			.find(|r| r.kind == b"method_call")
562			.expect("method_call ref");
563		assert_eq!(r.receiver_hint, b"obj".to_vec());
564		assert_eq!(r.target.as_view().segments().last().unwrap().name, b"Bar");
565	}
566
567	#[test]
568	fn extract_chained_method_call_receiver_hint_is_call() {
569		let src = "package foo\nfunc Run() { foo().bar() }\n";
570		let g = extract_default("foo.go", src, &make_anchor(), false);
571		let r = g
572			.refs()
573			.find(|r| {
574				r.kind == b"method_call"
575					&& r.target.as_view().segments().last().unwrap().name == b"bar"
576			})
577			.expect("method_call bar");
578		assert_eq!(r.receiver_hint, b"call".to_vec());
579	}
580
581	#[test]
582	fn extract_call_visits_arguments_for_nested_calls() {
583		let src = "package foo\nfunc Run() { Outer(Inner()) }\n";
584		let g = extract_default("foo.go", src, &make_anchor(), false);
585		let names: Vec<&[u8]> = g
586			.refs()
587			.filter(|r| r.kind == b"calls")
588			.map(|r| r.target.as_view().segments().last().unwrap().name)
589			.collect();
590		assert!(names.contains(&&b"Outer"[..]));
591		assert!(names.contains(&&b"Inner"[..]));
592	}
593
594	#[test]
595	fn extract_composite_literal_emits_instantiates() {
596		let src = "package foo\ntype Foo struct{ X int }\nfunc Run() { _ = Foo{X: 1} }\n";
597		let g = extract_default("foo.go", src, &make_anchor(), false);
598		let r = g
599			.refs()
600			.find(|r| r.kind == b"instantiates")
601			.expect("instantiates ref");
602		assert_eq!(r.target.as_view().segments().last().unwrap().name, b"Foo");
603		assert_eq!(r.confidence, b"resolved".to_vec());
604	}
605
606	#[test]
607	fn extract_qualified_composite_literal_uses_imported_path() {
608		let src = "package foo\nimport \"net/http\"\nfunc Run() { _ = http.Request{} }\n";
609		let g = extract_default("foo.go", src, &make_anchor(), false);
610		let r = g
611			.refs()
612			.find(|r| r.kind == b"instantiates")
613			.expect("instantiates ref");
614		let names: Vec<&[u8]> = r.target.as_view().segments().map(|s| s.name).collect();
615		assert_eq!(names, vec![&b"net"[..], &b"http"[..], &b"Request"[..]]);
616		assert_eq!(r.confidence, b"external".to_vec());
617	}
618
619	#[test]
620	fn extract_composite_literal_unresolved_type_marks_name_match() {
621		let src = "package foo\nfunc Run() { _ = Bar{} }\n";
622		let g = extract_default("foo.go", src, &make_anchor(), false);
623		let r = g
624			.refs()
625			.find(|r| r.kind == b"instantiates")
626			.expect("instantiates ref");
627		assert_eq!(r.confidence, b"name_match".to_vec());
628	}
629
630	#[test]
631	fn extract_param_type_emits_uses_type() {
632		let src = "package foo\ntype Bar struct{}\nfunc Run(x Bar) {}\n";
633		let g = extract_default("foo.go", src, &make_anchor(), false);
634		let r = g
635			.refs()
636			.find(|r| {
637				r.kind == b"uses_type"
638					&& r.target.as_view().segments().last().unwrap().name == b"Bar"
639			})
640			.expect("uses_type Bar");
641		assert_eq!(r.confidence, b"resolved".to_vec());
642	}
643
644	#[test]
645	fn extract_return_type_emits_uses_type() {
646		let src = "package foo\ntype Bar struct{}\nfunc Run() Bar { return Bar{} }\n";
647		let g = extract_default("foo.go", src, &make_anchor(), false);
648		assert!(g.refs().any(|r| r.kind == b"uses_type"
649			&& r.target.as_view().segments().last().unwrap().name == b"Bar"));
650	}
651
652	#[test]
653	fn extract_multi_return_emits_uses_type_for_each() {
654		let src =
655			"package foo\ntype A struct{}\ntype B struct{}\nfunc Run() (a A, b B) { return }\n";
656		let g = extract_default("foo.go", src, &make_anchor(), false);
657		let names: Vec<&[u8]> = g
658			.refs()
659			.filter(|r| r.kind == b"uses_type")
660			.map(|r| r.target.as_view().segments().last().unwrap().name)
661			.collect();
662		assert!(names.contains(&&b"A"[..]));
663		assert!(names.contains(&&b"B"[..]));
664	}
665
666	#[test]
667	fn extract_qualified_param_type_uses_imported_path() {
668		let src = "package foo\nimport \"net/http\"\nfunc Run(r http.Request) {}\n";
669		let g = extract_default("foo.go", src, &make_anchor(), false);
670		let r = g
671			.refs()
672			.find(|r| {
673				r.kind == b"uses_type"
674					&& r.target.as_view().segments().last().unwrap().name == b"Request"
675			})
676			.expect("uses_type Request");
677		assert_eq!(r.confidence, b"external".to_vec());
678		let names: Vec<&[u8]> = r.target.as_view().segments().map(|s| s.name).collect();
679		assert_eq!(names, vec![&b"net"[..], &b"http"[..], &b"Request"[..]]);
680	}
681
682	#[test]
683	fn extract_pointer_param_type_descends() {
684		let src = "package foo\ntype Bar struct{}\nfunc Run(x *Bar) {}\n";
685		let g = extract_default("foo.go", src, &make_anchor(), false);
686		assert!(g.refs().any(|r| r.kind == b"uses_type"
687			&& r.target.as_view().segments().last().unwrap().name == b"Bar"));
688	}
689
690	#[test]
691	fn extract_slice_param_type_descends() {
692		let src = "package foo\ntype Bar struct{}\nfunc Run(xs []Bar) {}\n";
693		let g = extract_default("foo.go", src, &make_anchor(), false);
694		assert!(g.refs().any(|r| r.kind == b"uses_type"
695			&& r.target.as_view().segments().last().unwrap().name == b"Bar"));
696	}
697
698	#[test]
699	fn extract_map_param_type_descends_into_key_and_value() {
700		let src = "package foo\ntype K struct{}\ntype V struct{}\nfunc Run(m map[K]V) {}\n";
701		let g = extract_default("foo.go", src, &make_anchor(), false);
702		let names: Vec<&[u8]> = g
703			.refs()
704			.filter(|r| r.kind == b"uses_type")
705			.map(|r| r.target.as_view().segments().last().unwrap().name)
706			.collect();
707		assert!(names.contains(&&b"K"[..]));
708		assert!(names.contains(&&b"V"[..]));
709	}
710
711	#[test]
712	fn extract_struct_field_type_emits_uses_type() {
713		let src = "package foo\ntype Bar struct{}\ntype Foo struct { x Bar }\n";
714		let g = extract_default("foo.go", src, &make_anchor(), false);
715		let r = g
716			.refs()
717			.find(|r| {
718				r.kind == b"uses_type"
719					&& r.target.as_view().segments().last().unwrap().name == b"Bar"
720			})
721			.expect("uses_type Bar");
722		let foo = MonikerBuilder::new()
723			.project(b"app")
724			.segment(b"lang", b"go")
725			.segment(b"module", b"foo")
726			.segment(b"struct", b"Foo")
727			.build();
728		assert_eq!(g.defs().nth(r.source).unwrap().moniker, foo);
729	}
730
731	#[test]
732	fn extract_struct_embedding_emits_extends() {
733		let src = "package foo\ntype Base struct{}\ntype Derived struct { Base; X int }\n";
734		let g = extract_default("foo.go", src, &make_anchor(), false);
735		let r = g
736			.refs()
737			.find(|r| r.kind == b"extends")
738			.expect("extends ref");
739		assert_eq!(r.target.as_view().segments().last().unwrap().name, b"Base");
740		let derived = MonikerBuilder::new()
741			.project(b"app")
742			.segment(b"lang", b"go")
743			.segment(b"module", b"foo")
744			.segment(b"struct", b"Derived")
745			.build();
746		assert_eq!(g.defs().nth(r.source).unwrap().moniker, derived);
747	}
748
749	#[test]
750	fn extract_pointer_embedding_strips_star_for_extends() {
751		let src = "package foo\ntype Base struct{}\ntype Derived struct { *Base }\n";
752		let g = extract_default("foo.go", src, &make_anchor(), false);
753		let r = g
754			.refs()
755			.find(|r| r.kind == b"extends")
756			.expect("extends ref");
757		assert_eq!(r.target.as_view().segments().last().unwrap().name, b"Base");
758	}
759
760	#[test]
761	fn extract_qualified_embedding_uses_imported_path() {
762		let src = "package foo\nimport \"net/http\"\ntype Wrapper struct { http.Request }\n";
763		let g = extract_default("foo.go", src, &make_anchor(), false);
764		let r = g
765			.refs()
766			.find(|r| r.kind == b"extends")
767			.expect("extends ref");
768		assert_eq!(r.confidence, b"external".to_vec());
769		let names: Vec<&[u8]> = r.target.as_view().segments().map(|s| s.name).collect();
770		assert_eq!(names, vec![&b"net"[..], &b"http"[..], &b"Request"[..]]);
771	}
772
773	#[test]
774	fn extract_interface_embedding_emits_extends() {
775		let src = "package foo\ntype Reader interface { Read() }\ntype ReadCloser interface { Reader; Close() }\n";
776		let g = extract_default("foo.go", src, &make_anchor(), false);
777		let r = g
778			.refs()
779			.find(|r| r.kind == b"extends")
780			.expect("extends ref");
781		assert_eq!(
782			r.target.as_view().segments().last().unwrap().name,
783			b"Reader"
784		);
785	}
786
787	#[test]
788	fn extract_type_alias_emits_uses_type_on_underlying() {
789		let src = "package foo\ntype Bar struct{}\ntype Aliased = Bar\n";
790		let g = extract_default("foo.go", src, &make_anchor(), false);
791		assert!(g.refs().any(|r| r.kind == b"uses_type"
792			&& r.target.as_view().segments().last().unwrap().name == b"Bar"));
793	}
794
795	#[test]
796	fn extract_shallow_skips_param_and_local_defs() {
797		let src = "package foo\nfunc Run(x int) { y := 1; _ = y }\n";
798		let g = extract_default("foo.go", src, &make_anchor(), false);
799		assert!(
800			g.defs().all(|d| d.kind != b"param" && d.kind != b"local"),
801			"shallow extraction must not emit param/local defs"
802		);
803	}
804
805	#[test]
806	fn extract_deep_emits_param_defs_under_function() {
807		let src = "package foo\nfunc Run(a int, b string) {}\n";
808		let g = extract_default("foo.go", src, &make_anchor(), true);
809		let pa = MonikerBuilder::new()
810			.project(b"app")
811			.segment(b"lang", b"go")
812			.segment(b"module", b"foo")
813			.segment(b"func", b"Run(a:int,b:string)")
814			.segment(b"param", b"a")
815			.build();
816		let pb = MonikerBuilder::new()
817			.project(b"app")
818			.segment(b"lang", b"go")
819			.segment(b"module", b"foo")
820			.segment(b"func", b"Run(a:int,b:string)")
821			.segment(b"param", b"b")
822			.build();
823		assert!(g.contains(&pa));
824		assert!(g.contains(&pb));
825	}
826
827	#[test]
828	fn extract_deep_emits_receiver_param_for_method() {
829		let src = "package foo\ntype Foo struct{}\nfunc (r *Foo) Bar(x int) {}\n";
830		let g = extract_default("foo.go", src, &make_anchor(), true);
831		let recv = MonikerBuilder::new()
832			.project(b"app")
833			.segment(b"lang", b"go")
834			.segment(b"module", b"foo")
835			.segment(b"struct", b"Foo")
836			.segment(b"method", b"Bar(x:int)")
837			.segment(b"param", b"r")
838			.build();
839		assert!(g.contains(&recv));
840	}
841
842	#[test]
843	fn extract_deep_skips_blank_param() {
844		let src = "package foo\nfunc Run(_ int, b string) {}\n";
845		let g = extract_default("foo.go", src, &make_anchor(), true);
846		let params: Vec<&[u8]> = g
847			.defs()
848			.filter(|d| d.kind == b"param")
849			.map(|d| d.moniker.as_view().segments().last().unwrap().name)
850			.collect();
851		assert_eq!(params, vec![&b"b"[..]]);
852	}
853
854	#[test]
855	fn extract_deep_emits_local_def_for_short_var() {
856		let src = "package foo\nfunc Run() { x := 1; _ = x }\n";
857		let g = extract_default("foo.go", src, &make_anchor(), true);
858		let lx = MonikerBuilder::new()
859			.project(b"app")
860			.segment(b"lang", b"go")
861			.segment(b"module", b"foo")
862			.segment(b"func", b"Run()")
863			.segment(b"local", b"x")
864			.build();
865		assert!(g.contains(&lx));
866	}
867
868	#[test]
869	fn extract_deep_emits_local_defs_for_multi_assign() {
870		let src = "package foo\nfunc Run() { x, y := 1, 2; _, _ = x, y }\n";
871		let g = extract_default("foo.go", src, &make_anchor(), true);
872		let names: Vec<&[u8]> = g
873			.defs()
874			.filter(|d| d.kind == b"local")
875			.map(|d| d.moniker.as_view().segments().last().unwrap().name)
876			.collect();
877		assert!(names.contains(&&b"x"[..]));
878		assert!(names.contains(&&b"y"[..]));
879	}
880
881	#[test]
882	fn extract_deep_emits_local_def_for_var_declaration() {
883		let src = "package foo\nfunc Run() { var z int = 5; _ = z }\n";
884		let g = extract_default("foo.go", src, &make_anchor(), true);
885		let lz = MonikerBuilder::new()
886			.project(b"app")
887			.segment(b"lang", b"go")
888			.segment(b"module", b"foo")
889			.segment(b"func", b"Run()")
890			.segment(b"local", b"z")
891			.build();
892		assert!(g.contains(&lz));
893	}
894
895	#[test]
896	fn extract_deep_emits_local_defs_for_range_vars() {
897		let src =
898			"package foo\nfunc Run(m map[string]int) { for k, v := range m { _, _ = k, v } }\n";
899		let g = extract_default("foo.go", src, &make_anchor(), true);
900		let names: Vec<&[u8]> = g
901			.defs()
902			.filter(|d| d.kind == b"local")
903			.map(|d| d.moniker.as_view().segments().last().unwrap().name)
904			.collect();
905		assert!(names.contains(&&b"k"[..]));
906		assert!(names.contains(&&b"v"[..]));
907	}
908
909	#[test]
910	fn extract_top_level_const_block_emits_one_def_per_name() {
911		let src = "package foo\ntype Key int\nconst (\n    a Key = iota\n    b\n    c\n)\n";
912		let g = extract_default("foo.go", src, &make_anchor(), false);
913		let names: Vec<&[u8]> = g
914			.defs()
915			.filter(|d| d.kind == b"const")
916			.map(|d| d.moniker.as_view().segments().last().unwrap().name)
917			.collect();
918		assert_eq!(
919			names,
920			vec![&b"a"[..], &b"b"[..], &b"c"[..]],
921			"each const_spec identifier must yield one const def at the module scope. defs: {:?}",
922			g.def_monikers()
923		);
924	}
925
926	#[test]
927	fn extract_top_level_var_block_emits_one_def_per_name() {
928		let src = "package foo\nvar (\n    x int\n    y string = \"hi\"\n)\n";
929		let g = extract_default("foo.go", src, &make_anchor(), false);
930		let names: Vec<&[u8]> = g
931			.defs()
932			.filter(|d| d.kind == b"var")
933			.map(|d| d.moniker.as_view().segments().last().unwrap().name)
934			.collect();
935		assert_eq!(names, vec![&b"x"[..], &b"y"[..]]);
936	}
937
938	#[test]
939	fn extract_top_level_var_does_not_pollute_locals() {
940		let src = "package foo\nvar GlobalCount int\nfunc Run() { GlobalCount = 1 }\n";
941		let g = extract_default("foo.go", src, &make_anchor(), false);
942		let local_names: Vec<&[u8]> = g
943			.defs()
944			.filter(|d| d.kind == b"local")
945			.map(|d| d.moniker.as_view().segments().last().unwrap().name)
946			.collect();
947		assert!(
948			local_names.is_empty(),
949			"a package-level var must not be emitted as a local. found locals: {:?}",
950			local_names
951		);
952		let vars: Vec<&[u8]> = g
953			.defs()
954			.filter(|d| d.kind == b"var")
955			.map(|d| d.moniker.as_view().segments().last().unwrap().name)
956			.collect();
957		assert_eq!(vars, vec![&b"GlobalCount"[..]]);
958	}
959
960	#[test]
961	fn extract_deep_skips_blank_in_short_var() {
962		let src = "package foo\nfunc Run() { _, y := 1, 2; _ = y }\n";
963		let g = extract_default("foo.go", src, &make_anchor(), true);
964		let names: Vec<&[u8]> = g
965			.defs()
966			.filter(|d| d.kind == b"local")
967			.map(|d| d.moniker.as_view().segments().last().unwrap().name)
968			.collect();
969		assert_eq!(names, vec![&b"y"[..]]);
970	}
971}