github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/ast/astutil/enclosing.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package astutil
     6  
     7  // This file defines utilities for working with source positions.
     8  
     9  import (
    10  	"fmt"
    11  	"go/ast"
    12  	"go/token"
    13  	"sort"
    14  
    15  	"github.com/powerman/golang-tools/internal/typeparams"
    16  )
    17  
    18  // PathEnclosingInterval returns the node that encloses the source
    19  // interval [start, end), and all its ancestors up to the AST root.
    20  //
    21  // The definition of "enclosing" used by this function considers
    22  // additional whitespace abutting a node to be enclosed by it.
    23  // In this example:
    24  //
    25  //              z := x + y // add them
    26  //                   <-A->
    27  //                  <----B----->
    28  //
    29  // the ast.BinaryExpr(+) node is considered to enclose interval B
    30  // even though its [Pos()..End()) is actually only interval A.
    31  // This behaviour makes user interfaces more tolerant of imperfect
    32  // input.
    33  //
    34  // This function treats tokens as nodes, though they are not included
    35  // in the result. e.g. PathEnclosingInterval("+") returns the
    36  // enclosing ast.BinaryExpr("x + y").
    37  //
    38  // If start==end, the 1-char interval following start is used instead.
    39  //
    40  // The 'exact' result is true if the interval contains only path[0]
    41  // and perhaps some adjacent whitespace.  It is false if the interval
    42  // overlaps multiple children of path[0], or if it contains only
    43  // interior whitespace of path[0].
    44  // In this example:
    45  //
    46  //              z := x + y // add them
    47  //                <--C-->     <---E-->
    48  //                  ^
    49  //                  D
    50  //
    51  // intervals C, D and E are inexact.  C is contained by the
    52  // z-assignment statement, because it spans three of its children (:=,
    53  // x, +).  So too is the 1-char interval D, because it contains only
    54  // interior whitespace of the assignment.  E is considered interior
    55  // whitespace of the BlockStmt containing the assignment.
    56  //
    57  // Precondition: [start, end) both lie within the same file as root.
    58  // TODO(adonovan): return (nil, false) in this case and remove precond.
    59  // Requires FileSet; see loader.tokenFileContainsPos.
    60  //
    61  // Postcondition: path is never nil; it always contains at least 'root'.
    62  //
    63  func PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Node, exact bool) {
    64  	// fmt.Printf("EnclosingInterval %d %d\n", start, end) // debugging
    65  
    66  	// Precondition: node.[Pos..End) and adjoining whitespace contain [start, end).
    67  	var visit func(node ast.Node) bool
    68  	visit = func(node ast.Node) bool {
    69  		path = append(path, node)
    70  
    71  		nodePos := node.Pos()
    72  		nodeEnd := node.End()
    73  
    74  		// fmt.Printf("visit(%T, %d, %d)\n", node, nodePos, nodeEnd) // debugging
    75  
    76  		// Intersect [start, end) with interval of node.
    77  		if start < nodePos {
    78  			start = nodePos
    79  		}
    80  		if end > nodeEnd {
    81  			end = nodeEnd
    82  		}
    83  
    84  		// Find sole child that contains [start, end).
    85  		children := childrenOf(node)
    86  		l := len(children)
    87  		for i, child := range children {
    88  			// [childPos, childEnd) is unaugmented interval of child.
    89  			childPos := child.Pos()
    90  			childEnd := child.End()
    91  
    92  			// [augPos, augEnd) is whitespace-augmented interval of child.
    93  			augPos := childPos
    94  			augEnd := childEnd
    95  			if i > 0 {
    96  				augPos = children[i-1].End() // start of preceding whitespace
    97  			}
    98  			if i < l-1 {
    99  				nextChildPos := children[i+1].Pos()
   100  				// Does [start, end) lie between child and next child?
   101  				if start >= augEnd && end <= nextChildPos {
   102  					return false // inexact match
   103  				}
   104  				augEnd = nextChildPos // end of following whitespace
   105  			}
   106  
   107  			// fmt.Printf("\tchild %d: [%d..%d)\tcontains interval [%d..%d)?\n",
   108  			// 	i, augPos, augEnd, start, end) // debugging
   109  
   110  			// Does augmented child strictly contain [start, end)?
   111  			if augPos <= start && end <= augEnd {
   112  				_, isToken := child.(tokenNode)
   113  				return isToken || visit(child)
   114  			}
   115  
   116  			// Does [start, end) overlap multiple children?
   117  			// i.e. left-augmented child contains start
   118  			// but LR-augmented child does not contain end.
   119  			if start < childEnd && end > augEnd {
   120  				break
   121  			}
   122  		}
   123  
   124  		// No single child contained [start, end),
   125  		// so node is the result.  Is it exact?
   126  
   127  		// (It's tempting to put this condition before the
   128  		// child loop, but it gives the wrong result in the
   129  		// case where a node (e.g. ExprStmt) and its sole
   130  		// child have equal intervals.)
   131  		if start == nodePos && end == nodeEnd {
   132  			return true // exact match
   133  		}
   134  
   135  		return false // inexact: overlaps multiple children
   136  	}
   137  
   138  	if start > end {
   139  		start, end = end, start
   140  	}
   141  
   142  	if start < root.End() && end > root.Pos() {
   143  		if start == end {
   144  			end = start + 1 // empty interval => interval of size 1
   145  		}
   146  		exact = visit(root)
   147  
   148  		// Reverse the path:
   149  		for i, l := 0, len(path); i < l/2; i++ {
   150  			path[i], path[l-1-i] = path[l-1-i], path[i]
   151  		}
   152  	} else {
   153  		// Selection lies within whitespace preceding the
   154  		// first (or following the last) declaration in the file.
   155  		// The result nonetheless always includes the ast.File.
   156  		path = append(path, root)
   157  	}
   158  
   159  	return
   160  }
   161  
   162  // tokenNode is a dummy implementation of ast.Node for a single token.
   163  // They are used transiently by PathEnclosingInterval but never escape
   164  // this package.
   165  //
   166  type tokenNode struct {
   167  	pos token.Pos
   168  	end token.Pos
   169  }
   170  
   171  func (n tokenNode) Pos() token.Pos {
   172  	return n.pos
   173  }
   174  
   175  func (n tokenNode) End() token.Pos {
   176  	return n.end
   177  }
   178  
   179  func tok(pos token.Pos, len int) ast.Node {
   180  	return tokenNode{pos, pos + token.Pos(len)}
   181  }
   182  
   183  // childrenOf returns the direct non-nil children of ast.Node n.
   184  // It may include fake ast.Node implementations for bare tokens.
   185  // it is not safe to call (e.g.) ast.Walk on such nodes.
   186  //
   187  func childrenOf(n ast.Node) []ast.Node {
   188  	var children []ast.Node
   189  
   190  	// First add nodes for all true subtrees.
   191  	ast.Inspect(n, func(node ast.Node) bool {
   192  		if node == n { // push n
   193  			return true // recur
   194  		}
   195  		if node != nil { // push child
   196  			children = append(children, node)
   197  		}
   198  		return false // no recursion
   199  	})
   200  
   201  	// Then add fake Nodes for bare tokens.
   202  	switch n := n.(type) {
   203  	case *ast.ArrayType:
   204  		children = append(children,
   205  			tok(n.Lbrack, len("[")),
   206  			tok(n.Elt.End(), len("]")))
   207  
   208  	case *ast.AssignStmt:
   209  		children = append(children,
   210  			tok(n.TokPos, len(n.Tok.String())))
   211  
   212  	case *ast.BasicLit:
   213  		children = append(children,
   214  			tok(n.ValuePos, len(n.Value)))
   215  
   216  	case *ast.BinaryExpr:
   217  		children = append(children, tok(n.OpPos, len(n.Op.String())))
   218  
   219  	case *ast.BlockStmt:
   220  		children = append(children,
   221  			tok(n.Lbrace, len("{")),
   222  			tok(n.Rbrace, len("}")))
   223  
   224  	case *ast.BranchStmt:
   225  		children = append(children,
   226  			tok(n.TokPos, len(n.Tok.String())))
   227  
   228  	case *ast.CallExpr:
   229  		children = append(children,
   230  			tok(n.Lparen, len("(")),
   231  			tok(n.Rparen, len(")")))
   232  		if n.Ellipsis != 0 {
   233  			children = append(children, tok(n.Ellipsis, len("...")))
   234  		}
   235  
   236  	case *ast.CaseClause:
   237  		if n.List == nil {
   238  			children = append(children,
   239  				tok(n.Case, len("default")))
   240  		} else {
   241  			children = append(children,
   242  				tok(n.Case, len("case")))
   243  		}
   244  		children = append(children, tok(n.Colon, len(":")))
   245  
   246  	case *ast.ChanType:
   247  		switch n.Dir {
   248  		case ast.RECV:
   249  			children = append(children, tok(n.Begin, len("<-chan")))
   250  		case ast.SEND:
   251  			children = append(children, tok(n.Begin, len("chan<-")))
   252  		case ast.RECV | ast.SEND:
   253  			children = append(children, tok(n.Begin, len("chan")))
   254  		}
   255  
   256  	case *ast.CommClause:
   257  		if n.Comm == nil {
   258  			children = append(children,
   259  				tok(n.Case, len("default")))
   260  		} else {
   261  			children = append(children,
   262  				tok(n.Case, len("case")))
   263  		}
   264  		children = append(children, tok(n.Colon, len(":")))
   265  
   266  	case *ast.Comment:
   267  		// nop
   268  
   269  	case *ast.CommentGroup:
   270  		// nop
   271  
   272  	case *ast.CompositeLit:
   273  		children = append(children,
   274  			tok(n.Lbrace, len("{")),
   275  			tok(n.Rbrace, len("{")))
   276  
   277  	case *ast.DeclStmt:
   278  		// nop
   279  
   280  	case *ast.DeferStmt:
   281  		children = append(children,
   282  			tok(n.Defer, len("defer")))
   283  
   284  	case *ast.Ellipsis:
   285  		children = append(children,
   286  			tok(n.Ellipsis, len("...")))
   287  
   288  	case *ast.EmptyStmt:
   289  		// nop
   290  
   291  	case *ast.ExprStmt:
   292  		// nop
   293  
   294  	case *ast.Field:
   295  		// TODO(adonovan): Field.{Doc,Comment,Tag}?
   296  
   297  	case *ast.FieldList:
   298  		children = append(children,
   299  			tok(n.Opening, len("(")), // or len("[")
   300  			tok(n.Closing, len(")"))) // or len("]")
   301  
   302  	case *ast.File:
   303  		// TODO test: Doc
   304  		children = append(children,
   305  			tok(n.Package, len("package")))
   306  
   307  	case *ast.ForStmt:
   308  		children = append(children,
   309  			tok(n.For, len("for")))
   310  
   311  	case *ast.FuncDecl:
   312  		// TODO(adonovan): FuncDecl.Comment?
   313  
   314  		// Uniquely, FuncDecl breaks the invariant that
   315  		// preorder traversal yields tokens in lexical order:
   316  		// in fact, FuncDecl.Recv precedes FuncDecl.Type.Func.
   317  		//
   318  		// As a workaround, we inline the case for FuncType
   319  		// here and order things correctly.
   320  		//
   321  		children = nil // discard ast.Walk(FuncDecl) info subtrees
   322  		children = append(children, tok(n.Type.Func, len("func")))
   323  		if n.Recv != nil {
   324  			children = append(children, n.Recv)
   325  		}
   326  		children = append(children, n.Name)
   327  		if tparams := typeparams.ForFuncType(n.Type); tparams != nil {
   328  			children = append(children, tparams)
   329  		}
   330  		if n.Type.Params != nil {
   331  			children = append(children, n.Type.Params)
   332  		}
   333  		if n.Type.Results != nil {
   334  			children = append(children, n.Type.Results)
   335  		}
   336  		if n.Body != nil {
   337  			children = append(children, n.Body)
   338  		}
   339  
   340  	case *ast.FuncLit:
   341  		// nop
   342  
   343  	case *ast.FuncType:
   344  		if n.Func != 0 {
   345  			children = append(children,
   346  				tok(n.Func, len("func")))
   347  		}
   348  
   349  	case *ast.GenDecl:
   350  		children = append(children,
   351  			tok(n.TokPos, len(n.Tok.String())))
   352  		if n.Lparen != 0 {
   353  			children = append(children,
   354  				tok(n.Lparen, len("(")),
   355  				tok(n.Rparen, len(")")))
   356  		}
   357  
   358  	case *ast.GoStmt:
   359  		children = append(children,
   360  			tok(n.Go, len("go")))
   361  
   362  	case *ast.Ident:
   363  		children = append(children,
   364  			tok(n.NamePos, len(n.Name)))
   365  
   366  	case *ast.IfStmt:
   367  		children = append(children,
   368  			tok(n.If, len("if")))
   369  
   370  	case *ast.ImportSpec:
   371  		// TODO(adonovan): ImportSpec.{Doc,EndPos}?
   372  
   373  	case *ast.IncDecStmt:
   374  		children = append(children,
   375  			tok(n.TokPos, len(n.Tok.String())))
   376  
   377  	case *ast.IndexExpr:
   378  		children = append(children,
   379  			tok(n.Lbrack, len("[")),
   380  			tok(n.Rbrack, len("]")))
   381  
   382  	case *typeparams.IndexListExpr:
   383  		children = append(children,
   384  			tok(n.Lbrack, len("[")),
   385  			tok(n.Rbrack, len("]")))
   386  
   387  	case *ast.InterfaceType:
   388  		children = append(children,
   389  			tok(n.Interface, len("interface")))
   390  
   391  	case *ast.KeyValueExpr:
   392  		children = append(children,
   393  			tok(n.Colon, len(":")))
   394  
   395  	case *ast.LabeledStmt:
   396  		children = append(children,
   397  			tok(n.Colon, len(":")))
   398  
   399  	case *ast.MapType:
   400  		children = append(children,
   401  			tok(n.Map, len("map")))
   402  
   403  	case *ast.ParenExpr:
   404  		children = append(children,
   405  			tok(n.Lparen, len("(")),
   406  			tok(n.Rparen, len(")")))
   407  
   408  	case *ast.RangeStmt:
   409  		children = append(children,
   410  			tok(n.For, len("for")),
   411  			tok(n.TokPos, len(n.Tok.String())))
   412  
   413  	case *ast.ReturnStmt:
   414  		children = append(children,
   415  			tok(n.Return, len("return")))
   416  
   417  	case *ast.SelectStmt:
   418  		children = append(children,
   419  			tok(n.Select, len("select")))
   420  
   421  	case *ast.SelectorExpr:
   422  		// nop
   423  
   424  	case *ast.SendStmt:
   425  		children = append(children,
   426  			tok(n.Arrow, len("<-")))
   427  
   428  	case *ast.SliceExpr:
   429  		children = append(children,
   430  			tok(n.Lbrack, len("[")),
   431  			tok(n.Rbrack, len("]")))
   432  
   433  	case *ast.StarExpr:
   434  		children = append(children, tok(n.Star, len("*")))
   435  
   436  	case *ast.StructType:
   437  		children = append(children, tok(n.Struct, len("struct")))
   438  
   439  	case *ast.SwitchStmt:
   440  		children = append(children, tok(n.Switch, len("switch")))
   441  
   442  	case *ast.TypeAssertExpr:
   443  		children = append(children,
   444  			tok(n.Lparen-1, len(".")),
   445  			tok(n.Lparen, len("(")),
   446  			tok(n.Rparen, len(")")))
   447  
   448  	case *ast.TypeSpec:
   449  		// TODO(adonovan): TypeSpec.{Doc,Comment}?
   450  
   451  	case *ast.TypeSwitchStmt:
   452  		children = append(children, tok(n.Switch, len("switch")))
   453  
   454  	case *ast.UnaryExpr:
   455  		children = append(children, tok(n.OpPos, len(n.Op.String())))
   456  
   457  	case *ast.ValueSpec:
   458  		// TODO(adonovan): ValueSpec.{Doc,Comment}?
   459  
   460  	case *ast.BadDecl, *ast.BadExpr, *ast.BadStmt:
   461  		// nop
   462  	}
   463  
   464  	// TODO(adonovan): opt: merge the logic of ast.Inspect() into
   465  	// the switch above so we can make interleaved callbacks for
   466  	// both Nodes and Tokens in the right order and avoid the need
   467  	// to sort.
   468  	sort.Sort(byPos(children))
   469  
   470  	return children
   471  }
   472  
   473  type byPos []ast.Node
   474  
   475  func (sl byPos) Len() int {
   476  	return len(sl)
   477  }
   478  func (sl byPos) Less(i, j int) bool {
   479  	return sl[i].Pos() < sl[j].Pos()
   480  }
   481  func (sl byPos) Swap(i, j int) {
   482  	sl[i], sl[j] = sl[j], sl[i]
   483  }
   484  
   485  // NodeDescription returns a description of the concrete type of n suitable
   486  // for a user interface.
   487  //
   488  // TODO(adonovan): in some cases (e.g. Field, FieldList, Ident,
   489  // StarExpr) we could be much more specific given the path to the AST
   490  // root.  Perhaps we should do that.
   491  //
   492  func NodeDescription(n ast.Node) string {
   493  	switch n := n.(type) {
   494  	case *ast.ArrayType:
   495  		return "array type"
   496  	case *ast.AssignStmt:
   497  		return "assignment"
   498  	case *ast.BadDecl:
   499  		return "bad declaration"
   500  	case *ast.BadExpr:
   501  		return "bad expression"
   502  	case *ast.BadStmt:
   503  		return "bad statement"
   504  	case *ast.BasicLit:
   505  		return "basic literal"
   506  	case *ast.BinaryExpr:
   507  		return fmt.Sprintf("binary %s operation", n.Op)
   508  	case *ast.BlockStmt:
   509  		return "block"
   510  	case *ast.BranchStmt:
   511  		switch n.Tok {
   512  		case token.BREAK:
   513  			return "break statement"
   514  		case token.CONTINUE:
   515  			return "continue statement"
   516  		case token.GOTO:
   517  			return "goto statement"
   518  		case token.FALLTHROUGH:
   519  			return "fall-through statement"
   520  		}
   521  	case *ast.CallExpr:
   522  		if len(n.Args) == 1 && !n.Ellipsis.IsValid() {
   523  			return "function call (or conversion)"
   524  		}
   525  		return "function call"
   526  	case *ast.CaseClause:
   527  		return "case clause"
   528  	case *ast.ChanType:
   529  		return "channel type"
   530  	case *ast.CommClause:
   531  		return "communication clause"
   532  	case *ast.Comment:
   533  		return "comment"
   534  	case *ast.CommentGroup:
   535  		return "comment group"
   536  	case *ast.CompositeLit:
   537  		return "composite literal"
   538  	case *ast.DeclStmt:
   539  		return NodeDescription(n.Decl) + " statement"
   540  	case *ast.DeferStmt:
   541  		return "defer statement"
   542  	case *ast.Ellipsis:
   543  		return "ellipsis"
   544  	case *ast.EmptyStmt:
   545  		return "empty statement"
   546  	case *ast.ExprStmt:
   547  		return "expression statement"
   548  	case *ast.Field:
   549  		// Can be any of these:
   550  		// struct {x, y int}  -- struct field(s)
   551  		// struct {T}         -- anon struct field
   552  		// interface {I}      -- interface embedding
   553  		// interface {f()}    -- interface method
   554  		// func (A) func(B) C -- receiver, param(s), result(s)
   555  		return "field/method/parameter"
   556  	case *ast.FieldList:
   557  		return "field/method/parameter list"
   558  	case *ast.File:
   559  		return "source file"
   560  	case *ast.ForStmt:
   561  		return "for loop"
   562  	case *ast.FuncDecl:
   563  		return "function declaration"
   564  	case *ast.FuncLit:
   565  		return "function literal"
   566  	case *ast.FuncType:
   567  		return "function type"
   568  	case *ast.GenDecl:
   569  		switch n.Tok {
   570  		case token.IMPORT:
   571  			return "import declaration"
   572  		case token.CONST:
   573  			return "constant declaration"
   574  		case token.TYPE:
   575  			return "type declaration"
   576  		case token.VAR:
   577  			return "variable declaration"
   578  		}
   579  	case *ast.GoStmt:
   580  		return "go statement"
   581  	case *ast.Ident:
   582  		return "identifier"
   583  	case *ast.IfStmt:
   584  		return "if statement"
   585  	case *ast.ImportSpec:
   586  		return "import specification"
   587  	case *ast.IncDecStmt:
   588  		if n.Tok == token.INC {
   589  			return "increment statement"
   590  		}
   591  		return "decrement statement"
   592  	case *ast.IndexExpr:
   593  		return "index expression"
   594  	case *typeparams.IndexListExpr:
   595  		return "index list expression"
   596  	case *ast.InterfaceType:
   597  		return "interface type"
   598  	case *ast.KeyValueExpr:
   599  		return "key/value association"
   600  	case *ast.LabeledStmt:
   601  		return "statement label"
   602  	case *ast.MapType:
   603  		return "map type"
   604  	case *ast.Package:
   605  		return "package"
   606  	case *ast.ParenExpr:
   607  		return "parenthesized " + NodeDescription(n.X)
   608  	case *ast.RangeStmt:
   609  		return "range loop"
   610  	case *ast.ReturnStmt:
   611  		return "return statement"
   612  	case *ast.SelectStmt:
   613  		return "select statement"
   614  	case *ast.SelectorExpr:
   615  		return "selector"
   616  	case *ast.SendStmt:
   617  		return "channel send"
   618  	case *ast.SliceExpr:
   619  		return "slice expression"
   620  	case *ast.StarExpr:
   621  		return "*-operation" // load/store expr or pointer type
   622  	case *ast.StructType:
   623  		return "struct type"
   624  	case *ast.SwitchStmt:
   625  		return "switch statement"
   626  	case *ast.TypeAssertExpr:
   627  		return "type assertion"
   628  	case *ast.TypeSpec:
   629  		return "type specification"
   630  	case *ast.TypeSwitchStmt:
   631  		return "type switch"
   632  	case *ast.UnaryExpr:
   633  		return fmt.Sprintf("unary %s operation", n.Op)
   634  	case *ast.ValueSpec:
   635  		return "value specification"
   636  
   637  	}
   638  	panic(fmt.Sprintf("unexpected node type: %T", n))
   639  }