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