Repository: prometheus/prometheus
File: promql/parser/ast.go (Go)

Code before:

// Visitor allows visiting a Node and its child nodes. The Visit method is
// invoked for each node with the path leading to the node provided additionally.
// If the result visitor w is not nil and no error, Walk visits each of the children
// of node with the visitor w, followed by a call of w.Visit(nil, nil).
type Visitor interface {
	Visit(node Node, path []Node) (w Visitor, err error)
}

// Walk traverses an AST in depth-first order: It starts by calling
// v.Visit(node, path); node must not be nil. If the visitor w returned by
// v.Visit(node, path) is not nil and the visitor returns no error, Walk is
// invoked recursively with visitor w for each of the non-nil children of node,
// followed by a call of w.Visit(nil), returning an error
// As the tree is descended the path of previous nodes is provided.
func Walk(v Visitor, node Node, path []Node) error {
	var err error
	if v, err = v.Visit(node, path); v == nil || err != nil {
		return err
	}
	var pathToHere []Node // Initialized only when needed.

	for e := range ChildrenIter(node) {
		if pathToHere == nil {
			pathToHere = append(path, node)
		}
		if err := Walk(v, e, pathToHere); err != nil {
			return err
		}
	}

	_, err = v.Visit(nil, nil)
	return err
}

func ExtractSelectors(expr Expr) [][]*labels.Matcher {
	var selectors [][]*labels.Matcher
	Inspect(expr, func(node Node, _ []Node) error {
		vs, ok := node.(*VectorSelector)
		if ok {
			selectors = append(selectors, vs.LabelMatchers)
		}
		return nil
	})
	return selectors
}

type inspector func(Node, []Node) error

func (f inspector) Visit(node Node, path []Node) (Visitor, error) {
	if err := f(node, path); err != nil {

Review comment (suggestion):
But this means that inspector functions passed to Inspect will not get the correct path for each call, breaking the contract. I agree with Charles that that seems like a different function or should change the contract.

Code after:
}

// Visitor allows visiting a Node and its child nodes. The Visit method is
// invoked for each node with the path leading to the node provided additionally.
// If the result visitor w is not nil and no error, Walk visits each of the children
// of node with the visitor w, followed by a call of w.Visit(nil, nil).
type Visitor interface {
	Visit(node Node, path []Node) (w Visitor, err error)
}

// Walk traverses an AST in depth-first order: It starts by calling
// v.Visit(node, path); node must not be nil. If the visitor w returned by
// v.Visit(node, path) is not nil and the visitor returns no error, Walk is
// invoked recursively with visitor w for each of the non-nil children of node,
// followed by a call of w.Visit(nil), returning an error
// As the tree is descended the path of previous nodes is provided.
func Walk(v Visitor, node Node, path []Node) error {
	var err error
	if v, err = v.Visit(node, path); v == nil || err != nil {
		return err
	}
	var pathToHere []Node // Initialized only when needed.

	for e := range ChildrenIter(node) {
		if pathToHere == nil {
			pathToHere = append(path, node)
		}
		if err := Walk(v, e, pathToHere); err != nil {
			return err
		}
	}

	_, err = v.Visit(nil, nil)
	return err
}

func ExtractSelectors(expr Expr) [][]*labels.Matcher {
	var selectors [][]*labels.Matcher
	Inspect(expr, func(node Node, _ []Node) error {
		vs, ok := node.(*VectorSelector)
		if ok {
			selectors = append(selectors, vs.LabelMatchers)
		}
		return nil
	})
	return selectors
}

type inspector func(Node, []Node) error

func (f inspector) Visit(node Node, path []Node) (Visitor, error) {