// PathEnclosingInterval returns the node that encloses the source // interval [start, end), and all its ancestors up to the AST root. // // The definition of "enclosing" used by this function considers // additional whitespace abutting a node to be enclosed by it. // In this example: // // z := x + y // add them // <-A-> // <----B-----> // // the ast.BinaryExpr(+) node is considered to enclose interval B // even though its [Pos()..End()) is actually only interval A. // This behaviour makes user interfaces more tolerant of imperfect // input. // // This function treats tokens as nodes, though they are not included // in the result. e.g. PathEnclosingInterval("+") returns the // enclosing ast.BinaryExpr("x + y"). // // If start==end, the 1-char interval following start is used instead. // // The 'exact' result is true if the interval contains only path[0] // and perhaps some adjacent whitespace. It is false if the interval // overlaps multiple children of path[0], or if it contains only // interior whitespace of path[0]. // In this example: // // z := x + y // add them // <--C--> <---E--> // ^ // D // // intervals C, D and E are inexact. C is contained by the // z-assignment statement, because it spans three of its children (:=, // x, +). So too is the 1-char interval D, because it contains only // interior whitespace of the assignment. E is considered interior // whitespace of the BlockStmt containing the assignment. // // The resulting path is never empty; it always contains at least the // 'root' *ast.File. Ideally PathEnclosingInterval would reject // intervals that lie wholly or partially outside the range of the // file, but unfortunately ast.File records only the token.Pos of // the 'package' keyword, but not of the start of the file itself. func PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Node, exact bool) { … } type tokenNode … func (n tokenNode) Pos() token.Pos { … } func (n tokenNode) End() token.Pos { … } func tok(pos token.Pos, len int) ast.Node { … } // childrenOf returns the direct non-nil children of ast.Node n. // It may include fake ast.Node implementations for bare tokens. // it is not safe to call (e.g.) ast.Walk on such nodes. func childrenOf(n ast.Node) []ast.Node { … } type byPos … func (sl byPos) Len() int { … } func (sl byPos) Less(i, j int) bool { … } func (sl byPos) Swap(i, j int) { … } // NodeDescription returns a description of the concrete type of n suitable // for a user interface. // // TODO(adonovan): in some cases (e.g. Field, FieldList, Ident, // StarExpr) we could be much more specific given the path to the AST // root. Perhaps we should do that. func NodeDescription(n ast.Node) string { … } func is[T any](x any) bool { … }