// 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 }