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