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}