mirror of https://github.com/golang/go.git
365 lines
6.5 KiB
Go
365 lines
6.5 KiB
Go
// Copyright 2020 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// This file implements helper functions for scope position computations.
|
|
|
|
package syntax
|
|
|
|
// StartPos returns the start position of n.
|
|
func StartPos(n Node) Pos {
|
|
// Cases for nodes which don't need a correction are commented out.
|
|
for m := n; ; {
|
|
switch n := m.(type) {
|
|
case nil:
|
|
panic("nil node")
|
|
|
|
// packages
|
|
case *File:
|
|
// file block starts at the beginning of the file
|
|
return MakePos(n.Pos().Base(), 1, 1)
|
|
|
|
// declarations
|
|
// case *ImportDecl:
|
|
// case *ConstDecl:
|
|
// case *TypeDecl:
|
|
// case *VarDecl:
|
|
// case *FuncDecl:
|
|
|
|
// expressions
|
|
// case *BadExpr:
|
|
// case *Name:
|
|
// case *BasicLit:
|
|
case *CompositeLit:
|
|
if n.Type != nil {
|
|
m = n.Type
|
|
continue
|
|
}
|
|
return n.Pos()
|
|
// case *KeyValueExpr:
|
|
// case *FuncLit:
|
|
// case *ParenExpr:
|
|
case *SelectorExpr:
|
|
m = n.X
|
|
case *IndexExpr:
|
|
m = n.X
|
|
// case *SliceExpr:
|
|
case *AssertExpr:
|
|
m = n.X
|
|
case *TypeSwitchGuard:
|
|
if n.Lhs != nil {
|
|
m = n.Lhs
|
|
continue
|
|
}
|
|
m = n.X
|
|
case *Operation:
|
|
if n.Y != nil {
|
|
m = n.X
|
|
continue
|
|
}
|
|
return n.Pos()
|
|
case *CallExpr:
|
|
m = n.Fun
|
|
case *ListExpr:
|
|
if len(n.ElemList) > 0 {
|
|
m = n.ElemList[0]
|
|
continue
|
|
}
|
|
return n.Pos()
|
|
// types
|
|
// case *ArrayType:
|
|
// case *SliceType:
|
|
// case *DotsType:
|
|
// case *StructType:
|
|
// case *Field:
|
|
// case *InterfaceType:
|
|
// case *FuncType:
|
|
// case *MapType:
|
|
// case *ChanType:
|
|
|
|
// statements
|
|
// case *EmptyStmt:
|
|
// case *LabeledStmt:
|
|
// case *BlockStmt:
|
|
// case *ExprStmt:
|
|
case *SendStmt:
|
|
m = n.Chan
|
|
// case *DeclStmt:
|
|
case *AssignStmt:
|
|
m = n.Lhs
|
|
// case *BranchStmt:
|
|
// case *CallStmt:
|
|
// case *ReturnStmt:
|
|
// case *IfStmt:
|
|
// case *ForStmt:
|
|
// case *SwitchStmt:
|
|
// case *SelectStmt:
|
|
|
|
// helper nodes
|
|
case *RangeClause:
|
|
if n.Lhs != nil {
|
|
m = n.Lhs
|
|
continue
|
|
}
|
|
m = n.X
|
|
// case *CaseClause:
|
|
// case *CommClause:
|
|
|
|
default:
|
|
return n.Pos()
|
|
}
|
|
}
|
|
}
|
|
|
|
// EndPos returns the approximate end position of n in the source.
|
|
// For some nodes (*Name, *BasicLit) it returns the position immediately
|
|
// following the node; for others (*BlockStmt, *SwitchStmt, etc.) it
|
|
// returns the position of the closing '}'; and for some (*ParenExpr)
|
|
// the returned position is the end position of the last enclosed
|
|
// expression.
|
|
// Thus, EndPos should not be used for exact demarcation of the
|
|
// end of a node in the source; it is mostly useful to determine
|
|
// scope ranges where there is some leeway.
|
|
func EndPos(n Node) Pos {
|
|
for m := n; ; {
|
|
switch n := m.(type) {
|
|
case nil:
|
|
panic("nil node")
|
|
|
|
// packages
|
|
case *File:
|
|
return n.EOF
|
|
|
|
// declarations
|
|
case *ImportDecl:
|
|
m = n.Path
|
|
case *ConstDecl:
|
|
if n.Values != nil {
|
|
m = n.Values
|
|
continue
|
|
}
|
|
if n.Type != nil {
|
|
m = n.Type
|
|
continue
|
|
}
|
|
if l := len(n.NameList); l > 0 {
|
|
m = n.NameList[l-1]
|
|
continue
|
|
}
|
|
return n.Pos()
|
|
case *TypeDecl:
|
|
m = n.Type
|
|
case *VarDecl:
|
|
if n.Values != nil {
|
|
m = n.Values
|
|
continue
|
|
}
|
|
if n.Type != nil {
|
|
m = n.Type
|
|
continue
|
|
}
|
|
if l := len(n.NameList); l > 0 {
|
|
m = n.NameList[l-1]
|
|
continue
|
|
}
|
|
return n.Pos()
|
|
case *FuncDecl:
|
|
if n.Body != nil {
|
|
m = n.Body
|
|
continue
|
|
}
|
|
m = n.Type
|
|
|
|
// expressions
|
|
case *BadExpr:
|
|
return n.Pos()
|
|
case *Name:
|
|
p := n.Pos()
|
|
return MakePos(p.Base(), p.Line(), p.Col()+uint(len(n.Value)))
|
|
case *BasicLit:
|
|
p := n.Pos()
|
|
return MakePos(p.Base(), p.Line(), p.Col()+uint(len(n.Value)))
|
|
case *CompositeLit:
|
|
return n.Rbrace
|
|
case *KeyValueExpr:
|
|
m = n.Value
|
|
case *FuncLit:
|
|
m = n.Body
|
|
case *ParenExpr:
|
|
m = n.X
|
|
case *SelectorExpr:
|
|
m = n.Sel
|
|
case *IndexExpr:
|
|
m = n.Index
|
|
case *SliceExpr:
|
|
for i := len(n.Index) - 1; i >= 0; i-- {
|
|
if x := n.Index[i]; x != nil {
|
|
m = x
|
|
continue
|
|
}
|
|
}
|
|
m = n.X
|
|
case *AssertExpr:
|
|
m = n.Type
|
|
case *TypeSwitchGuard:
|
|
m = n.X
|
|
case *Operation:
|
|
if n.Y != nil {
|
|
m = n.Y
|
|
continue
|
|
}
|
|
m = n.X
|
|
case *CallExpr:
|
|
if l := lastExpr(n.ArgList); l != nil {
|
|
m = l
|
|
continue
|
|
}
|
|
m = n.Fun
|
|
case *ListExpr:
|
|
if l := lastExpr(n.ElemList); l != nil {
|
|
m = l
|
|
continue
|
|
}
|
|
return n.Pos()
|
|
|
|
// types
|
|
case *ArrayType:
|
|
m = n.Elem
|
|
case *SliceType:
|
|
m = n.Elem
|
|
case *DotsType:
|
|
m = n.Elem
|
|
case *StructType:
|
|
if l := lastField(n.FieldList); l != nil {
|
|
m = l
|
|
continue
|
|
}
|
|
return n.Pos()
|
|
// TODO(gri) need to take TagList into account
|
|
case *Field:
|
|
if n.Type != nil {
|
|
m = n.Type
|
|
continue
|
|
}
|
|
m = n.Name
|
|
case *InterfaceType:
|
|
if l := lastField(n.MethodList); l != nil {
|
|
m = l
|
|
continue
|
|
}
|
|
return n.Pos()
|
|
case *FuncType:
|
|
if l := lastField(n.ResultList); l != nil {
|
|
m = l
|
|
continue
|
|
}
|
|
if l := lastField(n.ParamList); l != nil {
|
|
m = l
|
|
continue
|
|
}
|
|
return n.Pos()
|
|
case *MapType:
|
|
m = n.Value
|
|
case *ChanType:
|
|
m = n.Elem
|
|
|
|
// statements
|
|
case *EmptyStmt:
|
|
return n.Pos()
|
|
case *LabeledStmt:
|
|
m = n.Stmt
|
|
case *BlockStmt:
|
|
return n.Rbrace
|
|
case *ExprStmt:
|
|
m = n.X
|
|
case *SendStmt:
|
|
m = n.Value
|
|
case *DeclStmt:
|
|
if l := lastDecl(n.DeclList); l != nil {
|
|
m = l
|
|
continue
|
|
}
|
|
return n.Pos()
|
|
case *AssignStmt:
|
|
m = n.Rhs
|
|
if m == nil {
|
|
p := EndPos(n.Lhs)
|
|
return MakePos(p.Base(), p.Line(), p.Col()+2)
|
|
}
|
|
case *BranchStmt:
|
|
if n.Label != nil {
|
|
m = n.Label
|
|
continue
|
|
}
|
|
return n.Pos()
|
|
case *CallStmt:
|
|
m = n.Call
|
|
case *ReturnStmt:
|
|
if n.Results != nil {
|
|
m = n.Results
|
|
continue
|
|
}
|
|
return n.Pos()
|
|
case *IfStmt:
|
|
if n.Else != nil {
|
|
m = n.Else
|
|
continue
|
|
}
|
|
m = n.Then
|
|
case *ForStmt:
|
|
m = n.Body
|
|
case *SwitchStmt:
|
|
return n.Rbrace
|
|
case *SelectStmt:
|
|
return n.Rbrace
|
|
|
|
// helper nodes
|
|
case *RangeClause:
|
|
m = n.X
|
|
case *CaseClause:
|
|
if l := lastStmt(n.Body); l != nil {
|
|
m = l
|
|
continue
|
|
}
|
|
return n.Colon
|
|
case *CommClause:
|
|
if l := lastStmt(n.Body); l != nil {
|
|
m = l
|
|
continue
|
|
}
|
|
return n.Colon
|
|
|
|
default:
|
|
return n.Pos()
|
|
}
|
|
}
|
|
}
|
|
|
|
func lastDecl(list []Decl) Decl {
|
|
if l := len(list); l > 0 {
|
|
return list[l-1]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func lastExpr(list []Expr) Expr {
|
|
if l := len(list); l > 0 {
|
|
return list[l-1]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func lastStmt(list []Stmt) Stmt {
|
|
if l := len(list); l > 0 {
|
|
return list[l-1]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func lastField(list []*Field) *Field {
|
|
if l := len(list); l > 0 {
|
|
return list[l-1]
|
|
}
|
|
return nil
|
|
}
|