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 }