1use tree_sitter::Node;
4
5use super::{field_text, line, named_children, text, LangSpec, RawContract, Target};
6use crate::facts::{Args, Import, ImportItem, RefKind, TypeSource, ValueExpr};
7use crate::model::SymbolKind;
8
9pub struct Go;
10
11const ROUTE_METHODS: &[&str] = &[
13 "HandleFunc",
14 "Handle",
15 "GET",
16 "POST",
17 "PUT",
18 "PATCH",
19 "DELETE",
20 "Get",
21 "Post",
22 "Put",
23 "Patch",
24 "Delete",
25];
26
27impl LangSpec for Go {
28 fn receiver(&self) -> &'static str {
29 "self"
30 }
31
32 fn contract(&self, node: Node, src: &[u8]) -> Option<RawContract> {
33 if node.kind() == "short_var_declaration" {
35 let left = named_children(node.child_by_field_name("left")?);
36 let right = named_children(node.child_by_field_name("right")?);
37 let ([name], [call]) = (left.as_slice(), right.as_slice()) else {
38 return None;
39 };
40 if name.kind() != "identifier" || call.kind() != "call_expression" {
41 return None;
42 }
43 let ctor = path(call.child_by_field_name("function")?, src)?;
44 if ctor.last().map(String::as_str) != Some("Group") {
45 return None;
46 }
47 return Some(RawContract::Construct {
48 target: Target::Var(text(*name, src).to_string()),
49 ctor,
50 args: args(call.child_by_field_name("arguments")?, src),
51 });
52 }
53 if node.kind() != "call_expression" {
54 return None;
55 }
56 let f = node.child_by_field_name("function")?;
57 let p = path(f, src)?;
58 let (method, receiver) = p.split_last()?;
59 let list = node.child_by_field_name("arguments")?;
60 let args = args(list, src);
61 if receiver == ["http"] && !method.starts_with("Handle") {
62 return Some(RawContract::Op {
63 receiver: receiver.to_vec(),
64 method: method.clone(),
65 args,
66 });
67 }
68 if !ROUTE_METHODS.contains(&method.as_str()) || args.positional.len() < 2 {
69 return None;
70 }
71 let Some(ValueExpr::Lit(pattern)) = args.positional.first().cloned().flatten() else {
72 return None;
73 };
74 let (verb, route) = match pattern.split_once(' ') {
75 Some((v, r)) if v.chars().all(|c| c.is_ascii_uppercase()) => (v.to_string(), r),
76 _ if method.starts_with("Handle") => ("ANY".to_string(), pattern.as_str()),
77 _ => (method.to_uppercase(), pattern.as_str()),
78 };
79 if !route.starts_with('/') {
80 return None;
81 }
82 let handler = named_children(list)
83 .last()
84 .filter(|h| h.kind() != "func_literal")
85 .and_then(|h| path(*h, src))
86 .unwrap_or_default();
87 let (prefix, receiver) = route_scope(node, receiver.to_vec(), src);
88 Some(RawContract::Handle {
89 receiver,
90 method: verb,
91 path: ValueExpr::Lit(format!("{prefix}{route}")),
92 handler,
93 })
94 }
95
96 fn value(&self, node: Node, src: &[u8]) -> Option<(String, ValueExpr)> {
97 if !matches!(node.kind(), "const_spec" | "var_spec") {
98 return None;
99 }
100 let name = field_text(node, "name", src)?.to_string();
101 let v = named_children(node.child_by_field_name("value")?)
102 .into_iter()
103 .next()?;
104 Some((name, value(v, src)?))
105 }
106
107 fn def(&self, node: Node, src: &[u8], _in_class: bool) -> Option<(SymbolKind, String)> {
108 let kind = match node.kind() {
109 "function_declaration" => SymbolKind::Function,
110 "method_declaration" | "method_elem" => SymbolKind::Method,
111 "type_spec" => match node.child_by_field_name("type")?.kind() {
112 "struct_type" => SymbolKind::Struct,
113 "interface_type" => SymbolKind::Interface,
114 _ => SymbolKind::TypeAlias,
115 },
116 "type_alias" => SymbolKind::TypeAlias,
117 _ => return None,
118 };
119 Some((kind, field_text(node, "name", src)?.to_string()))
120 }
121
122 fn owner(&self, def: Node, src: &[u8]) -> Option<String> {
123 if def.kind() != "method_declaration" {
124 return None;
125 }
126 let recv = named_children(def.child_by_field_name("receiver")?)
127 .into_iter()
128 .find(|p| p.kind() == "parameter_declaration")?;
129 type_path(recv.child_by_field_name("type")?, src)?.pop()
130 }
131
132 fn variable(&self, node: Node, src: &[u8]) -> Option<String> {
133 matches!(node.kind(), "var_spec" | "const_spec")
134 .then(|| field_text(node, "name", src).map(str::to_string))?
135 }
136
137 fn import(&self, node: Node, src: &[u8]) -> Option<Vec<Import>> {
138 if node.kind() != "import_declaration" {
139 return None;
140 }
141 let mut specs = Vec::new();
142 for c in named_children(node) {
143 match c.kind() {
144 "import_spec" => specs.push(c),
145 "import_spec_list" => specs.extend(
146 named_children(c)
147 .into_iter()
148 .filter(|s| s.kind() == "import_spec"),
149 ),
150 _ => {}
151 }
152 }
153 let imports = specs
154 .into_iter()
155 .filter_map(|s| {
156 let module = field_text(s, "path", src)?
157 .trim_matches(|c| c == '"' || c == '`')
158 .to_string();
159 let item = match s
160 .child_by_field_name("name")
161 .map(|n| (n.kind(), text(n, src)))
162 {
163 Some(("dot", _)) => ImportItem::All,
164 Some(("blank_identifier", _)) => ImportItem::Module { alias: None },
165 Some((_, alias)) => ImportItem::Module {
166 alias: Some(alias.to_string()),
167 },
168 None => ImportItem::Module {
169 alias: Some(default_name(&module).to_string()),
170 },
171 };
172 Some(Import {
173 module,
174 level: 0,
175 items: vec![item],
176 line: line(s),
177 file: None,
178 })
179 })
180 .collect();
181 Some(imports)
182 }
183
184 fn reference(&self, node: Node, src: &[u8]) -> Option<(RefKind, Vec<String>)> {
185 match node.kind() {
186 "call_expression" => Some((
187 RefKind::Call,
188 path(node.child_by_field_name("function")?, src)?,
189 )),
190 "composite_literal" => Some((
191 RefKind::New,
192 type_path(node.child_by_field_name("type")?, src)?,
193 )),
194 _ => None,
195 }
196 }
197
198 fn assignment(&self, node: Node, src: &[u8]) -> Option<(Target, TypeSource)> {
199 match node.kind() {
200 "short_var_declaration" => {
201 let left = named_children(node.child_by_field_name("left")?)
202 .into_iter()
203 .next()?;
204 if left.kind() != "identifier" {
205 return None;
206 }
207 let right = named_children(node.child_by_field_name("right")?)
208 .into_iter()
209 .next()?;
210 Some((
211 Target::Var(text(left, src).to_string()),
212 value_type(right, src)?,
213 ))
214 }
215 "var_spec" => {
216 let name = field_text(node, "name", src)?.to_string();
217 if let Some(t) = node
218 .child_by_field_name("type")
219 .and_then(|t| type_path(t, src))
220 {
221 return Some((Target::Var(name), TypeSource::Annotation(t)));
222 }
223 let v = named_children(node.child_by_field_name("value")?)
224 .into_iter()
225 .next()?;
226 Some((Target::Var(name), value_type(v, src)?))
227 }
228 "field_declaration" => Some((
229 Target::Attr(field_text(node, "name", src)?.to_string()),
230 TypeSource::Annotation(type_path(node.child_by_field_name("type")?, src)?),
231 )),
232 _ => None,
233 }
234 }
235
236 fn bases(&self, def: Node, src: &[u8]) -> Vec<(RefKind, Vec<String>)> {
237 let Some(ty) = (def.kind() == "type_spec")
238 .then(|| def.child_by_field_name("type"))
239 .flatten()
240 else {
241 return Vec::new();
242 };
243 let mut out = Vec::new();
244 let mut stack = vec![ty];
245 while let Some(n) = stack.pop() {
246 match n.kind() {
247 "field_declaration" if n.child_by_field_name("name").is_none() => {
249 if let Some(p) = n
250 .child_by_field_name("type")
251 .and_then(|t| type_path(t, src))
252 {
253 out.push((RefKind::Inherits, p));
254 }
255 }
256 "type_elem" => out.extend(
257 named_children(n)
258 .into_iter()
259 .filter_map(|t| type_path(t, src))
260 .map(|p| (RefKind::Inherits, p)),
261 ),
262 "struct_type" | "interface_type" | "field_declaration_list" => {
263 stack.extend(named_children(n))
264 }
265 _ => {}
266 }
267 }
268 out
269 }
270
271 fn returns(&self, def: Node, src: &[u8]) -> Option<Vec<String>> {
272 let r = def.child_by_field_name("result")?;
273 if r.kind() == "parameter_list" {
274 let first = named_children(r)
275 .into_iter()
276 .find(|p| p.kind() == "parameter_declaration")?;
277 return type_path(first.child_by_field_name("type")?, src);
278 }
279 type_path(r, src)
280 }
281
282 fn params(&self, def: Node, src: &[u8]) -> Vec<(String, Vec<String>, bool)> {
283 let lists = ["receiver", "parameters"]
284 .into_iter()
285 .filter_map(|f| def.child_by_field_name(f));
286 let mut out = Vec::new();
287 for list in lists {
288 for p in named_children(list)
289 .into_iter()
290 .filter(|p| p.kind() == "parameter_declaration")
291 {
292 let Some(ty) = p
293 .child_by_field_name("type")
294 .and_then(|t| type_path(t, src))
295 else {
296 continue;
297 };
298 let mut cur = p.walk();
299 for n in p.children_by_field_name("name", &mut cur) {
300 out.push((text(n, src).to_string(), ty.clone(), false));
301 }
302 }
303 }
304 out
305 }
306}
307
308fn default_name(module: &str) -> &str {
310 let mut segs = module.rsplit('/');
311 let last = segs.next().unwrap_or(module);
312 let is_version =
313 last.len() > 1 && last.starts_with('v') && last[1..].chars().all(|c| c.is_ascii_digit());
314 let name = if is_version {
315 segs.next().unwrap_or(last)
316 } else {
317 last
318 };
319 name.split('.').next().unwrap_or(name)
321}
322
323fn value_type(v: Node, src: &[u8]) -> Option<TypeSource> {
325 match v.kind() {
326 "call_expression" => Some(TypeSource::Construct(path(
327 v.child_by_field_name("function")?,
328 src,
329 )?)),
330 "composite_literal" => Some(TypeSource::Construct(type_path(
331 v.child_by_field_name("type")?,
332 src,
333 )?)),
334 "unary_expression" => value_type(v.child_by_field_name("operand")?, src),
335 "identifier" => Some(TypeSource::Alias {
336 name: text(v, src).to_string(),
337 scope: String::new(),
338 }),
339 _ => None,
340 }
341}
342
343fn route_scope(node: Node, mut receiver: Vec<String>, src: &[u8]) -> (String, Vec<String>) {
346 let mut prefix = String::new();
347 let mut cur = node.parent();
348 while let Some(n) = cur {
349 cur = n.parent();
350 if n.kind() != "func_literal" {
351 continue;
352 }
353 let param = n
354 .child_by_field_name("parameters")
355 .and_then(|ps| named_children(ps).into_iter().next())
356 .and_then(|p| p.child_by_field_name("name"))
357 .map(|i| text(i, src).to_string());
358 let call = n
359 .parent()
360 .filter(|a| a.kind() == "argument_list")
361 .and_then(|a| a.parent())
362 .filter(|c| c.kind() == "call_expression");
363 let (Some(param), Some(call)) = (param, call) else {
364 continue;
365 };
366 if receiver != [param] {
367 continue;
368 }
369 let Some(p) = call
370 .child_by_field_name("function")
371 .and_then(|f| path(f, src))
372 else {
373 continue;
374 };
375 let Some((m, outer)) = p.split_last() else {
376 continue;
377 };
378 let lit = call
379 .child_by_field_name("arguments")
380 .and_then(|a| named_children(a).into_iter().next())
381 .and_then(|a| value(a, src));
382 if let (true, Some(ValueExpr::Lit(l))) = (m == "Route", lit) {
383 prefix = format!("{l}{prefix}");
384 receiver = outer.to_vec();
385 }
386 }
387 (prefix, receiver)
388}
389
390fn value(node: Node, src: &[u8]) -> Option<ValueExpr> {
391 match node.kind() {
392 "interpreted_string_literal" | "raw_string_literal" => Some(ValueExpr::Lit(
393 text(node, src)
394 .trim_matches(|c| c == '"' || c == '`')
395 .to_string(),
396 )),
397 "identifier" | "selector_expression" => path(node, src).map(ValueExpr::Name),
398 "binary_expression" if field_text(node, "operator", src) == Some("+") => {
399 Some(ValueExpr::Join {
400 sep: String::new(),
401 parts: [
402 node.child_by_field_name("left"),
403 node.child_by_field_name("right"),
404 ]
405 .into_iter()
406 .map(|n| {
407 n.and_then(|n| value(n, src))
408 .unwrap_or(ValueExpr::Name(Vec::new()))
409 })
410 .collect(),
411 })
412 }
413 "call_expression" => {
414 let func = path(node.child_by_field_name("function")?, src)?;
415 let a = args(node.child_by_field_name("arguments")?, src);
416 Some(ValueExpr::Call {
417 func,
418 args: a
419 .positional
420 .into_iter()
421 .map(|v| v.unwrap_or(ValueExpr::Name(Vec::new())))
422 .collect(),
423 kwargs: Vec::new(),
424 })
425 }
426 "parenthesized_expression" => value(*named_children(node).first()?, src),
427 _ => None,
428 }
429}
430
431fn args(list: Node, src: &[u8]) -> Args {
432 let mut out = Args::default();
433 for a in named_children(list) {
434 if a.kind() == "comment" {
435 continue;
436 }
437 out.positional.push(value(a, src));
438 }
439 out
440}
441
442fn path(node: Node, src: &[u8]) -> Option<Vec<String>> {
443 match node.kind() {
444 "identifier" | "field_identifier" | "type_identifier" | "package_identifier" => {
445 Some(vec![text(node, src).to_string()])
446 }
447 "selector_expression" => {
448 let mut p = path(node.child_by_field_name("operand")?, src)?;
449 p.push(field_text(node, "field", src)?.to_string());
450 Some(p)
451 }
452 _ => None,
453 }
454}
455
456fn type_path(node: Node, src: &[u8]) -> Option<Vec<String>> {
457 match node.kind() {
458 "qualified_type" => Some(vec![
459 field_text(node, "package", src)?.to_string(),
460 field_text(node, "name", src)?.to_string(),
461 ]),
462 "pointer_type" | "parenthesized_type" => {
463 type_path(named_children(node).into_iter().next()?, src)
464 }
465 "generic_type" => type_path(node.child_by_field_name("type")?, src),
466 _ => path(node, src),
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use super::default_name;
473
474 #[test]
475 fn go_default_import_names() {
476 assert_eq!(default_name("github.com/acme/golib/store"), "store");
477 assert_eq!(default_name("github.com/jackc/pgx/v5"), "pgx");
478 assert_eq!(default_name("gopkg.in/yaml.v3"), "yaml");
479 assert_eq!(default_name("fmt"), "fmt");
480 }
481}