Coverage for parsers/go.py: 74%

1"""Go tree-sitter parser and function collection."""

2

3 from __future__ import annotations

4

5 import tree_sitter_go as tsgo

6 from tree_sitter import Language

7

8 from parsers.common import parse_with_language

9 from parsers.shared import create_parse, create_collect_functions

10

11 GO_LANGUAGE = Language(tsgo.language())

12

13 parse = create_parse(GO_LANGUAGE)

14

15

16 def _collect_functions_recursive(node, acc: list) -> None:

17 if node.type in ("function_declaration", "method_declaration"):

18 acc.append(node)

19 elif node.type == "func_literal":

20 if is_named_func_literal(node):

21 acc.append(node)

22 for child in node.children:

23 _collect_functions_recursive(child, acc)

24

25

26 collect_functions = create_collect_functions(_collect_functions_recursive)

27

28

29 def is_named_func_literal(node) -> bool:

30 """Check if a func_literal is assigned to a variable (named closure).

31

32 Matches patterns like:

33 f := func() {} (short_var_declaration)

34 var f = func() {} (var_spec)

35 """

36 if node.type != "func_literal":

37 return False

38 parent = node.parent

39 if parent is None:

40 return False

41 if (

42 parent.type == "expression_list"

43 and parent.parent

44 and parent.parent.type == "short_var_declaration"

45 ):

46 return True

47 if parent.type == "var_spec":

48 return True

49 return False

50

51

52 def func_name(node) -> str:

53 """Extract function name from a function node."""

54 if node.type in ("function_declaration", "method_declaration"):

55 name_node = node.child_by_field_name("name")

56 if name_node:

57 return name_node.text.decode()

58 return "<anonymous>"

59 if node.type == "func_literal":

60 parent = node.parent

61 if parent is None:

62 return "<anonymous>"

63 if (

64 parent.type == "expression_list"

65 and parent.parent

66 and parent.parent.type == "short_var_declaration"

67 ):

68 svd = parent.parent

69 left = svd.child_by_field_name("left")

70 if left:

71 for child in left.children:

72 if child.type == "identifier":

73 return child.text.decode()

74 if parent.type == "var_spec":

75 name_node = parent.child_by_field_name("name")

76 if name_node:

77 return name_node.text.decode()

78 for child in parent.children:

79 if child.type == "identifier":

80 return child.text.decode()

81 return "<anonymous>"

82 return "<anonymous>"