1 Frequently Used Package

Go comes with a big bunch of packages to help user to parse and process Go code. Here I just list some frequently used ones with some brief description:

Especially for code analysis, go provides a set of analysis packages:

99 Tips

99.1 Lossy Comment Handling During AST -> Source Code

type example struct {
    foo int

    // this is a lossy comment

    bar int
}

Each field is of type *ast.Field. This struct has a *ast.Field.Comment field that contains the comment for that specific field.

But for the example above, to whom does it belong? Is it a part of foo or bar?

Because it’s impossible to determine it, these comments are called loosely comments. Now the problem occurs if you print the struct above with the format.Node() function. When you print it, this is what you get:

type example struct {
    foo int

    bar int
}

The problem is that lossy comments are part of the *ast.File and hold separated from the tree. And those are only printed when you print the whole file. So the workaround this is to print the whole file and then cut out the specific lines we want to return.

99.2 Replace ast Node

By leveraging the astutil.Apply(), we can replace or manipulate node in a AST. However, something to notice that:

  1. If you replace a node, then astutil.Apply() will not traverse both the new node and old node. So if you want to recursively apply the containing node of the new node, you need to explicitly call astutil.Apply() on the new node.
  2. If you replace a node via astutil.Apply() during ast.Walk(), then ast.Walk() will not traverse both the new noed and the old node. If you still want to walk the new node, you need to explicitly call ast.Walk() on the new node. (same for ast.Inspect())

99.3 Fill ast node parent

Refered to gogrep.

type nodeList interface {
	at(i int) ast.Node
	len() int
	slice(from, to int) nodeList
	ast.Node
}

func (m *matcher) fillParents(nodes ...ast.Node) {
	stack := make([]ast.Node, 1, 32)
	for _, node := range nodes {
		inspect(node, func(node ast.Node) bool {
			if node == nil {
				stack = stack[:len(stack)-1]
				return true
			}
			if _, ok := node.(nodeList); !ok {
				m.parents[node] = stack[len(stack)-1]
			}
			stack = append(stack, node)
			return true
		})
	}
}

func (m *matcher) matches(cmds []exprCmd, nodes []ast.Node) []ast.Node {
	m.parents = make(map[ast.Node]ast.Node)
    m.fillParents(nodes...)
    //...
}