Source File
ast.go
Belonging Package
go/ast
// Copyright 2009 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.
// Package ast declares the types used to represent syntax trees for Go
// packages.
package ast
import (
)
// ----------------------------------------------------------------------------
// Interfaces
//
// There are 3 main classes of nodes: Expressions and type nodes,
// statement nodes, and declaration nodes. The node names usually
// match the corresponding Go spec production names to which they
// correspond. The node fields correspond to the individual parts
// of the respective productions.
//
// All nodes contain position information marking the beginning of
// the corresponding source text segment; it is accessible via the
// Pos accessor method. Nodes may contain additional position info
// for language constructs where comments may be found between parts
// of the construct (typically any larger, parenthesized subpart).
// That position information is needed to properly position comments
// when printing the construct.
// All node types implement the Node interface.
type Node interface {
Pos() token.Pos // position of first character belonging to the node
End() token.Pos // position of first character immediately after the node
}
// All expression nodes implement the Expr interface.
type Expr interface {
Node
exprNode()
}
// All statement nodes implement the Stmt interface.
type Stmt interface {
Node
stmtNode()
}
// All declaration nodes implement the Decl interface.
type Decl interface {
Node
declNode()
}
// ----------------------------------------------------------------------------
// Comments
// A Comment node represents a single //-style or /*-style comment.
//
// The Text field contains the comment text without carriage returns (\r) that
// may have been present in the source. Because a comment's end position is
// computed using len(Text), the position reported by [Comment.End] does not match the
// true source end position for comments containing carriage returns.
type Comment struct {
Slash token.Pos // position of "/" starting the comment
Text string // comment text (excluding '\n' for //-style comments)
}
func ( *Comment) () token.Pos { return .Slash }
func ( *Comment) () token.Pos { return token.Pos(int(.Slash) + len(.Text)) }
// A CommentGroup represents a sequence of comments
// with no other tokens and no empty lines between.
type CommentGroup struct {
List []*Comment // len(List) > 0
}
func ( *CommentGroup) () token.Pos { return .List[0].Pos() }
func ( *CommentGroup) () token.Pos { return .List[len(.List)-1].End() }
func isWhitespace( byte) bool { return == ' ' || == '\t' || == '\n' || == '\r' }
func stripTrailingWhitespace( string) string {
:= len()
for > 0 && isWhitespace([-1]) {
--
}
return [0:]
}
// Text returns the text of the comment.
// Comment markers (//, /*, and */), the first space of a line comment, and
// leading and trailing empty lines are removed.
// Comment directives like "//line" and "//go:noinline" are also removed.
// Multiple empty lines are reduced to one, and trailing space on lines is trimmed.
// Unless the result is empty, it is newline-terminated.
func ( *CommentGroup) () string {
if == nil {
return ""
}
:= make([]string, len(.List))
for , := range .List {
[] = .Text
}
:= make([]string, 0, 10) // most comments are less than 10 lines
for , := range {
// Remove comment markers.
// The parser has given us exactly the comment text.
switch [1] {
case '/':
//-style comment (no newline at the end)
= [2:]
if len() == 0 {
// empty line
break
}
if [0] == ' ' {
// strip first space - required for Example tests
= [1:]
break
}
if isDirective() {
// Ignore //go:noinline, //line, and so on.
continue
}
case '*':
/*-style comment */
= [2 : len()-2]
}
// Split on newlines.
:= strings.Split(, "\n")
// Walk lines, stripping trailing white space and adding to list.
for , := range {
= append(, stripTrailingWhitespace())
}
}
// Remove leading blank lines; convert runs of
// interior blank lines to a single blank line.
:= 0
for , := range {
if != "" || > 0 && [-1] != "" {
[] =
++
}
}
= [0:]
// Add final "" entry to get trailing newline from Join.
if > 0 && [-1] != "" {
= append(, "")
}
return strings.Join(, "\n")
}
// isDirective reports whether c is a comment directive.
// This code is also in go/printer.
func isDirective( string) bool {
// "//line " is a line directive.
// "//extern " is for gccgo.
// "//export " is for cgo.
// (The // has been removed.)
if strings.HasPrefix(, "line ") || strings.HasPrefix(, "extern ") || strings.HasPrefix(, "export ") {
return true
}
// "//[a-z0-9]+:[a-z0-9]"
// (The // has been removed.)
:= strings.Index(, ":")
if <= 0 || +1 >= len() {
return false
}
for := 0; <= +1; ++ {
if == {
continue
}
:= []
if !('a' <= && <= 'z' || '0' <= && <= '9') {
return false
}
}
return true
}
// ----------------------------------------------------------------------------
// Expressions and types
// A Field represents a Field declaration list in a struct type,
// a method list in an interface type, or a parameter/result declaration
// in a signature.
// [Field.Names] is nil for unnamed parameters (parameter lists which only contain types)
// and embedded struct fields. In the latter case, the field name is the type name.
type Field struct {
Doc *CommentGroup // associated documentation; or nil
Names []*Ident // field/method/(type) parameter names; or nil
Type Expr // field/method/parameter type; or nil
Tag *BasicLit // field tag; or nil
Comment *CommentGroup // line comments; or nil
}
func ( *Field) () token.Pos {
if len(.Names) > 0 {
return .Names[0].Pos()
}
if .Type != nil {
return .Type.Pos()
}
return token.NoPos
}
func ( *Field) () token.Pos {
if .Tag != nil {
return .Tag.End()
}
if .Type != nil {
return .Type.End()
}
if len(.Names) > 0 {
return .Names[len(.Names)-1].End()
}
return token.NoPos
}
// A FieldList represents a list of Fields, enclosed by parentheses,
// curly braces, or square brackets.
type FieldList struct {
Opening token.Pos // position of opening parenthesis/brace/bracket, if any
List []*Field // field list; or nil
Closing token.Pos // position of closing parenthesis/brace/bracket, if any
}
func ( *FieldList) () token.Pos {
if .Opening.IsValid() {
return .Opening
}
// the list should not be empty in this case;
// be conservative and guard against bad ASTs
if len(.List) > 0 {
return .List[0].Pos()
}
return token.NoPos
}
func ( *FieldList) () token.Pos {
if .Closing.IsValid() {
return .Closing + 1
}
// the list should not be empty in this case;
// be conservative and guard against bad ASTs
if := len(.List); > 0 {
return .List[-1].End()
}
return token.NoPos
}
// NumFields returns the number of parameters or struct fields represented by a [FieldList].
func ( *FieldList) () int {
:= 0
if != nil {
for , := range .List {
:= len(.Names)
if == 0 {
= 1
}
+=
}
}
return
}
// An expression is represented by a tree consisting of one
// or more of the following concrete expression nodes.
type (
// A BadExpr node is a placeholder for an expression containing
// syntax errors for which a correct expression node cannot be
// created.
//
BadExpr struct {
From, To token.Pos // position range of bad expression
}
// An Ident node represents an identifier.
Ident struct {
NamePos token.Pos // identifier position
Name string // identifier name
Obj *Object // denoted object, or nil. Deprecated: see Object.
}
// An Ellipsis node stands for the "..." type in a
// parameter list or the "..." length in an array type.
//
Ellipsis struct {
Ellipsis token.Pos // position of "..."
Elt Expr // ellipsis element type (parameter lists only); or nil
}
// A BasicLit node represents a literal of basic type.
BasicLit struct {
ValuePos token.Pos // literal position
Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
Value string // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
}
// A FuncLit node represents a function literal.
FuncLit struct {
Type *FuncType // function type
Body *BlockStmt // function body
}
// A CompositeLit node represents a composite literal.
CompositeLit struct {
Type Expr // literal type; or nil
Lbrace token.Pos // position of "{"
Elts []Expr // list of composite elements; or nil
Rbrace token.Pos // position of "}"
Incomplete bool // true if (source) expressions are missing in the Elts list
}
// A ParenExpr node represents a parenthesized expression.
ParenExpr struct {
Lparen token.Pos // position of "("
X Expr // parenthesized expression
Rparen token.Pos // position of ")"
}
// A SelectorExpr node represents an expression followed by a selector.
SelectorExpr struct {
X Expr // expression
Sel *Ident // field selector
}
// An IndexExpr node represents an expression followed by an index.
IndexExpr struct {
X Expr // expression
Lbrack token.Pos // position of "["
Index Expr // index expression
Rbrack token.Pos // position of "]"
}
// An IndexListExpr node represents an expression followed by multiple
// indices.
IndexListExpr struct {
X Expr // expression
Lbrack token.Pos // position of "["
Indices []Expr // index expressions
Rbrack token.Pos // position of "]"
}
// A SliceExpr node represents an expression followed by slice indices.
SliceExpr struct {
X Expr // expression
Lbrack token.Pos // position of "["
Low Expr // begin of slice range; or nil
High Expr // end of slice range; or nil
Max Expr // maximum capacity of slice; or nil
Slice3 bool // true if 3-index slice (2 colons present)
Rbrack token.Pos // position of "]"
}
// A TypeAssertExpr node represents an expression followed by a
// type assertion.
//
TypeAssertExpr struct {
X Expr // expression
Lparen token.Pos // position of "("
Type Expr // asserted type; nil means type switch X.(type)
Rparen token.Pos // position of ")"
}
// A CallExpr node represents an expression followed by an argument list.
CallExpr struct {
Fun Expr // function expression
Lparen token.Pos // position of "("
Args []Expr // function arguments; or nil
Ellipsis token.Pos // position of "..." (token.NoPos if there is no "...")
Rparen token.Pos // position of ")"
}
// A StarExpr node represents an expression of the form "*" Expression.
// Semantically it could be a unary "*" expression, or a pointer type.
//
StarExpr struct {
Star token.Pos // position of "*"
X Expr // operand
}
// A UnaryExpr node represents a unary expression.
// Unary "*" expressions are represented via StarExpr nodes.
//
UnaryExpr struct {
OpPos token.Pos // position of Op
Op token.Token // operator
X Expr // operand
}
// A BinaryExpr node represents a binary expression.
BinaryExpr struct {
X Expr // left operand
OpPos token.Pos // position of Op
Op token.Token // operator
Y Expr // right operand
}
// A KeyValueExpr node represents (key : value) pairs
// in composite literals.
//
KeyValueExpr struct {
Key Expr
Colon token.Pos // position of ":"
Value Expr
}
)
// The direction of a channel type is indicated by a bit
// mask including one or both of the following constants.
type ChanDir int
const (
SEND ChanDir = 1 << iota
RECV
)
// A type is represented by a tree consisting of one
// or more of the following type-specific expression
// nodes.
type (
// An ArrayType node represents an array or slice type.
ArrayType struct {
Lbrack token.Pos // position of "["
Len Expr // Ellipsis node for [...]T array types, nil for slice types
Elt Expr // element type
}
// A StructType node represents a struct type.
StructType struct {
Struct token.Pos // position of "struct" keyword
Fields *FieldList // list of field declarations
Incomplete bool // true if (source) fields are missing in the Fields list
}
// Pointer types are represented via StarExpr nodes.
// A FuncType node represents a function type.
FuncType struct {
Func token.Pos // position of "func" keyword (token.NoPos if there is no "func")
TypeParams *FieldList // type parameters; or nil
Params *FieldList // (incoming) parameters; non-nil
Results *FieldList // (outgoing) results; or nil
}
// An InterfaceType node represents an interface type.
InterfaceType struct {
Interface token.Pos // position of "interface" keyword
Methods *FieldList // list of embedded interfaces, methods, or types
Incomplete bool // true if (source) methods or types are missing in the Methods list
}
// A MapType node represents a map type.
MapType struct {
Map token.Pos // position of "map" keyword
Key Expr
Value Expr
}
// A ChanType node represents a channel type.
ChanType struct {
Begin token.Pos // position of "chan" keyword or "<-" (whichever comes first)
Arrow token.Pos // position of "<-" (token.NoPos if there is no "<-")
Dir ChanDir // channel direction
Value Expr // value type
}
)
// Pos and End implementations for expression/type nodes.
func ( *BadExpr) () token.Pos { return .From }
func ( *Ident) () token.Pos { return .NamePos }
func ( *Ellipsis) () token.Pos { return .Ellipsis }
func ( *BasicLit) () token.Pos { return .ValuePos }
func ( *FuncLit) () token.Pos { return .Type.Pos() }
func ( *CompositeLit) () token.Pos {
if .Type != nil {
return .Type.Pos()
}
return .Lbrace
}
func ( *ParenExpr) () token.Pos { return .Lparen }
func ( *SelectorExpr) () token.Pos { return .X.Pos() }
func ( *IndexExpr) () token.Pos { return .X.Pos() }
func ( *IndexListExpr) () token.Pos { return .X.Pos() }
func ( *SliceExpr) () token.Pos { return .X.Pos() }
func ( *TypeAssertExpr) () token.Pos { return .X.Pos() }
func ( *CallExpr) () token.Pos { return .Fun.Pos() }
func ( *StarExpr) () token.Pos { return .Star }
func ( *UnaryExpr) () token.Pos { return .OpPos }
func ( *BinaryExpr) () token.Pos { return .X.Pos() }
func ( *KeyValueExpr) () token.Pos { return .Key.Pos() }
func ( *ArrayType) () token.Pos { return .Lbrack }
func ( *StructType) () token.Pos { return .Struct }
func ( *FuncType) () token.Pos {
if .Func.IsValid() || .Params == nil { // see issue 3870
return .Func
}
return .Params.Pos() // interface method declarations have no "func" keyword
}
func ( *InterfaceType) () token.Pos { return .Interface }
func ( *MapType) () token.Pos { return .Map }
func ( *ChanType) () token.Pos { return .Begin }
func ( *BadExpr) () token.Pos { return .To }
func ( *Ident) () token.Pos { return token.Pos(int(.NamePos) + len(.Name)) }
func ( *Ellipsis) () token.Pos {
if .Elt != nil {
return .Elt.End()
}
return .Ellipsis + 3 // len("...")
}
func ( *BasicLit) () token.Pos { return token.Pos(int(.ValuePos) + len(.Value)) }
func ( *FuncLit) () token.Pos { return .Body.End() }
func ( *CompositeLit) () token.Pos { return .Rbrace + 1 }
func ( *ParenExpr) () token.Pos { return .Rparen + 1 }
func ( *SelectorExpr) () token.Pos { return .Sel.End() }
func ( *IndexExpr) () token.Pos { return .Rbrack + 1 }
func ( *IndexListExpr) () token.Pos { return .Rbrack + 1 }
func ( *SliceExpr) () token.Pos { return .Rbrack + 1 }
func ( *TypeAssertExpr) () token.Pos { return .Rparen + 1 }
func ( *CallExpr) () token.Pos { return .Rparen + 1 }
func ( *StarExpr) () token.Pos { return .X.End() }
func ( *UnaryExpr) () token.Pos { return .X.End() }
func ( *BinaryExpr) () token.Pos { return .Y.End() }
func ( *KeyValueExpr) () token.Pos { return .Value.End() }
func ( *ArrayType) () token.Pos { return .Elt.End() }
func ( *StructType) () token.Pos { return .Fields.End() }
func ( *FuncType) () token.Pos {
if .Results != nil {
return .Results.End()
}
return .Params.End()
}
func ( *InterfaceType) () token.Pos { return .Methods.End() }
func ( *MapType) () token.Pos { return .Value.End() }
func ( *ChanType) () token.Pos { return .Value.End() }
// exprNode() ensures that only expression/type nodes can be
// assigned to an Expr.
func (*BadExpr) () {}
func (*Ident) () {}
func (*Ellipsis) () {}
func (*BasicLit) () {}
func (*FuncLit) () {}
func (*CompositeLit) () {}
func (*ParenExpr) () {}
func (*SelectorExpr) () {}
func (*IndexExpr) () {}
func (*IndexListExpr) () {}
func (*SliceExpr) () {}
func (*TypeAssertExpr) () {}
func (*CallExpr) () {}
func (*StarExpr) () {}
func (*UnaryExpr) () {}
func (*BinaryExpr) () {}
func (*KeyValueExpr) () {}
func (*ArrayType) () {}
func (*StructType) () {}
func (*FuncType) () {}
func (*InterfaceType) () {}
func (*MapType) () {}
func (*ChanType) () {}
// ----------------------------------------------------------------------------
// Convenience functions for Idents
// NewIdent creates a new [Ident] without position.
// Useful for ASTs generated by code other than the Go parser.
func ( string) *Ident { return &Ident{token.NoPos, , nil} }
// IsExported reports whether name starts with an upper-case letter.
func ( string) bool { return token.IsExported() }
// IsExported reports whether id starts with an upper-case letter.
func ( *Ident) () bool { return token.IsExported(.Name) }
func ( *Ident) () string {
if != nil {
return .Name
}
return "<nil>"
}
// ----------------------------------------------------------------------------
// Statements
// A statement is represented by a tree consisting of one
// or more of the following concrete statement nodes.
type (
// A BadStmt node is a placeholder for statements containing
// syntax errors for which no correct statement nodes can be
// created.
//
BadStmt struct {
From, To token.Pos // position range of bad statement
}
// A DeclStmt node represents a declaration in a statement list.
DeclStmt struct {
Decl Decl // *GenDecl with CONST, TYPE, or VAR token
}
// An EmptyStmt node represents an empty statement.
// The "position" of the empty statement is the position
// of the immediately following (explicit or implicit) semicolon.
//
EmptyStmt struct {
Semicolon token.Pos // position of following ";"
Implicit bool // if set, ";" was omitted in the source
}
// A LabeledStmt node represents a labeled statement.
LabeledStmt struct {
Label *Ident
Colon token.Pos // position of ":"
Stmt Stmt
}
// An ExprStmt node represents a (stand-alone) expression
// in a statement list.
//
ExprStmt struct {
X Expr // expression
}
// A SendStmt node represents a send statement.
SendStmt struct {
Chan Expr
Arrow token.Pos // position of "<-"
Value Expr
}
// An IncDecStmt node represents an increment or decrement statement.
IncDecStmt struct {
X Expr
TokPos token.Pos // position of Tok
Tok token.Token // INC or DEC
}
// An AssignStmt node represents an assignment or
// a short variable declaration.
//
AssignStmt struct {
Lhs []Expr
TokPos token.Pos // position of Tok
Tok token.Token // assignment token, DEFINE
Rhs []Expr
}
// A GoStmt node represents a go statement.
GoStmt struct {
Go token.Pos // position of "go" keyword
Call *CallExpr
}
// A DeferStmt node represents a defer statement.
DeferStmt struct {
Defer token.Pos // position of "defer" keyword
Call *CallExpr
}
// A ReturnStmt node represents a return statement.
ReturnStmt struct {
Return token.Pos // position of "return" keyword
Results []Expr // result expressions; or nil
}
// A BranchStmt node represents a break, continue, goto,
// or fallthrough statement.
//
BranchStmt struct {
TokPos token.Pos // position of Tok
Tok token.Token // keyword token (BREAK, CONTINUE, GOTO, FALLTHROUGH)
Label *Ident // label name; or nil
}
// A BlockStmt node represents a braced statement list.
BlockStmt struct {
Lbrace token.Pos // position of "{"
List []Stmt
Rbrace token.Pos // position of "}", if any (may be absent due to syntax error)
}
// An IfStmt node represents an if statement.
IfStmt struct {
If token.Pos // position of "if" keyword
Init Stmt // initialization statement; or nil
Cond Expr // condition
Body *BlockStmt
Else Stmt // else branch; or nil
}
// A CaseClause represents a case of an expression or type switch statement.
CaseClause struct {
Case token.Pos // position of "case" or "default" keyword
List []Expr // list of expressions or types; nil means default case
Colon token.Pos // position of ":"
Body []Stmt // statement list; or nil
}
// A SwitchStmt node represents an expression switch statement.
SwitchStmt struct {
Switch token.Pos // position of "switch" keyword
Init Stmt // initialization statement; or nil
Tag Expr // tag expression; or nil
Body *BlockStmt // CaseClauses only
}
// A TypeSwitchStmt node represents a type switch statement.
TypeSwitchStmt struct {
Switch token.Pos // position of "switch" keyword
Init Stmt // initialization statement; or nil
Assign Stmt // x := y.(type) or y.(type)
Body *BlockStmt // CaseClauses only
}
// A CommClause node represents a case of a select statement.
CommClause struct {
Case token.Pos // position of "case" or "default" keyword
Comm Stmt // send or receive statement; nil means default case
Colon token.Pos // position of ":"
Body []Stmt // statement list; or nil
}
// A SelectStmt node represents a select statement.
SelectStmt struct {
Select token.Pos // position of "select" keyword
Body *BlockStmt // CommClauses only
}
// A ForStmt represents a for statement.
ForStmt struct {
For token.Pos // position of "for" keyword
Init Stmt // initialization statement; or nil
Cond Expr // condition; or nil
Post Stmt // post iteration statement; or nil
Body *BlockStmt
}
// A RangeStmt represents a for statement with a range clause.
RangeStmt struct {
For token.Pos // position of "for" keyword
Key, Value Expr // Key, Value may be nil
TokPos token.Pos // position of Tok; invalid if Key == nil
Tok token.Token // ILLEGAL if Key == nil, ASSIGN, DEFINE
Range token.Pos // position of "range" keyword
X Expr // value to range over
Body *BlockStmt
}
)
// Pos and End implementations for statement nodes.
func ( *BadStmt) () token.Pos { return .From }
func ( *DeclStmt) () token.Pos { return .Decl.Pos() }
func ( *EmptyStmt) () token.Pos { return .Semicolon }
func ( *LabeledStmt) () token.Pos { return .Label.Pos() }
func ( *ExprStmt) () token.Pos { return .X.Pos() }
func ( *SendStmt) () token.Pos { return .Chan.Pos() }
func ( *IncDecStmt) () token.Pos { return .X.Pos() }
func ( *AssignStmt) () token.Pos { return .Lhs[0].Pos() }
func ( *GoStmt) () token.Pos { return .Go }
func ( *DeferStmt) () token.Pos { return .Defer }
func ( *ReturnStmt) () token.Pos { return .Return }
func ( *BranchStmt) () token.Pos { return .TokPos }
func ( *BlockStmt) () token.Pos { return .Lbrace }
func ( *IfStmt) () token.Pos { return .If }
func ( *CaseClause) () token.Pos { return .Case }
func ( *SwitchStmt) () token.Pos { return .Switch }
func ( *TypeSwitchStmt) () token.Pos { return .Switch }
func ( *CommClause) () token.Pos { return .Case }
func ( *SelectStmt) () token.Pos { return .Select }
func ( *ForStmt) () token.Pos { return .For }
func ( *RangeStmt) () token.Pos { return .For }
func ( *BadStmt) () token.Pos { return .To }
func ( *DeclStmt) () token.Pos { return .Decl.End() }
func ( *EmptyStmt) () token.Pos {
if .Implicit {
return .Semicolon
}
return .Semicolon + 1 /* len(";") */
}
func ( *LabeledStmt) () token.Pos { return .Stmt.End() }
func ( *ExprStmt) () token.Pos { return .X.End() }
func ( *SendStmt) () token.Pos { return .Value.End() }
func ( *IncDecStmt) () token.Pos {
return .TokPos + 2 /* len("++") */
}
func ( *AssignStmt) () token.Pos { return .Rhs[len(.Rhs)-1].End() }
func ( *GoStmt) () token.Pos { return .Call.End() }
func ( *DeferStmt) () token.Pos { return .Call.End() }
func ( *ReturnStmt) () token.Pos {
if := len(.Results); > 0 {
return .Results[-1].End()
}
return .Return + 6 // len("return")
}
func ( *BranchStmt) () token.Pos {
if .Label != nil {
return .Label.End()
}
return token.Pos(int(.TokPos) + len(.Tok.String()))
}
func ( *BlockStmt) () token.Pos {
if .Rbrace.IsValid() {
return .Rbrace + 1
}
if := len(.List); > 0 {
return .List[-1].End()
}
return .Lbrace + 1
}
func ( *IfStmt) () token.Pos {
if .Else != nil {
return .Else.End()
}
return .Body.End()
}
func ( *CaseClause) () token.Pos {
if := len(.Body); > 0 {
return .Body[-1].End()
}
return .Colon + 1
}
func ( *SwitchStmt) () token.Pos { return .Body.End() }
func ( *TypeSwitchStmt) () token.Pos { return .Body.End() }
func ( *CommClause) () token.Pos {
if := len(.Body); > 0 {
return .Body[-1].End()
}
return .Colon + 1
}
func ( *SelectStmt) () token.Pos { return .Body.End() }
func ( *ForStmt) () token.Pos { return .Body.End() }
func ( *RangeStmt) () token.Pos { return .Body.End() }
// stmtNode() ensures that only statement nodes can be
// assigned to a Stmt.
func (*BadStmt) () {}
func (*DeclStmt) () {}
func (*EmptyStmt) () {}
func (*LabeledStmt) () {}
func (*ExprStmt) () {}
func (*SendStmt) () {}
func (*IncDecStmt) () {}
func (*AssignStmt) () {}
func (*GoStmt) () {}
func (*DeferStmt) () {}
func (*ReturnStmt) () {}
func (*BranchStmt) () {}
func (*BlockStmt) () {}
func (*IfStmt) () {}
func (*CaseClause) () {}
func (*SwitchStmt) () {}
func (*TypeSwitchStmt) () {}
func (*CommClause) () {}
func (*SelectStmt) () {}
func (*ForStmt) () {}
func (*RangeStmt) () {}
// ----------------------------------------------------------------------------
// Declarations
// A Spec node represents a single (non-parenthesized) import,
// constant, type, or variable declaration.
type (
// The Spec type stands for any of *ImportSpec, *ValueSpec, and *TypeSpec.
Spec interface {
Node
specNode()
}
// An ImportSpec node represents a single package import.
ImportSpec struct {
Doc *CommentGroup // associated documentation; or nil
Name *Ident // local package name (including "."); or nil
Path *BasicLit // import path
Comment *CommentGroup // line comments; or nil
EndPos token.Pos // end of spec (overrides Path.Pos if nonzero)
}
// A ValueSpec node represents a constant or variable declaration
// (ConstSpec or VarSpec production).
//
ValueSpec struct {
Doc *CommentGroup // associated documentation; or nil
Names []*Ident // value names (len(Names) > 0)
Type Expr // value type; or nil
Values []Expr // initial values; or nil
Comment *CommentGroup // line comments; or nil
}
// A TypeSpec node represents a type declaration (TypeSpec production).
TypeSpec struct {
Doc *CommentGroup // associated documentation; or nil
Name *Ident // type name
TypeParams *FieldList // type parameters; or nil
Assign token.Pos // position of '=', if any
Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
Comment *CommentGroup // line comments; or nil
}
)
// Pos and End implementations for spec nodes.
func ( *ImportSpec) () token.Pos {
if .Name != nil {
return .Name.Pos()
}
return .Path.Pos()
}
func ( *ValueSpec) () token.Pos { return .Names[0].Pos() }
func ( *TypeSpec) () token.Pos { return .Name.Pos() }
func ( *ImportSpec) () token.Pos {
if .EndPos != 0 {
return .EndPos
}
return .Path.End()
}
func ( *ValueSpec) () token.Pos {
if := len(.Values); > 0 {
return .Values[-1].End()
}
if .Type != nil {
return .Type.End()
}
return .Names[len(.Names)-1].End()
}
func ( *TypeSpec) () token.Pos { return .Type.End() }
// specNode() ensures that only spec nodes can be
// assigned to a Spec.
func (*ImportSpec) () {}
func (*ValueSpec) () {}
func (*TypeSpec) () {}
// A declaration is represented by one of the following declaration nodes.
type (
// A BadDecl node is a placeholder for a declaration containing
// syntax errors for which a correct declaration node cannot be
// created.
//
BadDecl struct {
From, To token.Pos // position range of bad declaration
}
// A GenDecl node (generic declaration node) represents an import,
// constant, type or variable declaration. A valid Lparen position
// (Lparen.IsValid()) indicates a parenthesized declaration.
//
// Relationship between Tok value and Specs element type:
//
// token.IMPORT *ImportSpec
// token.CONST *ValueSpec
// token.TYPE *TypeSpec
// token.VAR *ValueSpec
//
GenDecl struct {
Doc *CommentGroup // associated documentation; or nil
TokPos token.Pos // position of Tok
Tok token.Token // IMPORT, CONST, TYPE, or VAR
Lparen token.Pos // position of '(', if any
Specs []Spec
Rparen token.Pos // position of ')', if any
}
// A FuncDecl node represents a function declaration.
FuncDecl struct {
Doc *CommentGroup // associated documentation; or nil
Recv *FieldList // receiver (methods); or nil (functions)
Name *Ident // function/method name
Type *FuncType // function signature: type and value parameters, results, and position of "func" keyword
Body *BlockStmt // function body; or nil for external (non-Go) function
}
)
// Pos and End implementations for declaration nodes.
func ( *BadDecl) () token.Pos { return .From }
func ( *GenDecl) () token.Pos { return .TokPos }
func ( *FuncDecl) () token.Pos { return .Type.Pos() }
func ( *BadDecl) () token.Pos { return .To }
func ( *GenDecl) () token.Pos {
if .Rparen.IsValid() {
return .Rparen + 1
}
return .Specs[0].End()
}
func ( *FuncDecl) () token.Pos {
if .Body != nil {
return .Body.End()
}
return .Type.End()
}
// declNode() ensures that only declaration nodes can be
// assigned to a Decl.
func (*BadDecl) () {}
func (*GenDecl) () {}
func (*FuncDecl) () {}
// ----------------------------------------------------------------------------
// Files and packages
// A File node represents a Go source file.
//
// The Comments list contains all comments in the source file in order of
// appearance, including the comments that are pointed to from other nodes
// via Doc and Comment fields.
//
// For correct printing of source code containing comments (using packages
// go/format and go/printer), special care must be taken to update comments
// when a File's syntax tree is modified: For printing, comments are interspersed
// between tokens based on their position. If syntax tree nodes are
// removed or moved, relevant comments in their vicinity must also be removed
// (from the [File.Comments] list) or moved accordingly (by updating their
// positions). A [CommentMap] may be used to facilitate some of these operations.
//
// Whether and how a comment is associated with a node depends on the
// interpretation of the syntax tree by the manipulating program: except for Doc
// and [Comment] comments directly associated with nodes, the remaining comments
// are "free-floating" (see also issues [#18593], [#20744]).
//
// [#18593]: https://go.dev/issue/18593
// [#20744]: https://go.dev/issue/20744
type File struct {
Doc *CommentGroup // associated documentation; or nil
Package token.Pos // position of "package" keyword
Name *Ident // package name
Decls []Decl // top-level declarations; or nil
FileStart, FileEnd token.Pos // start and end of entire file
Scope *Scope // package scope (this file only). Deprecated: see Object
Imports []*ImportSpec // imports in this file
Unresolved []*Ident // unresolved identifiers in this file. Deprecated: see Object
Comments []*CommentGroup // list of all comments in the source file
GoVersion string // minimum Go version required by //go:build or // +build directives
}
// Pos returns the position of the package declaration.
// (Use FileStart for the start of the entire file.)
func ( *File) () token.Pos { return .Package }
// End returns the end of the last declaration in the file.
// (Use FileEnd for the end of the entire file.)
func ( *File) () token.Pos {
if := len(.Decls); > 0 {
return .Decls[-1].End()
}
return .Name.End()
}
// A Package node represents a set of source files
// collectively building a Go package.
//
// Deprecated: use the type checker [go/types] instead; see [Object].
type Package struct {
Name string // package name
Scope *Scope // package scope across all files
Imports map[string]*Object // map of package id -> package object
Files map[string]*File // Go source files by filename
}
func ( *Package) () token.Pos { return token.NoPos }
func ( *Package) () token.Pos { return token.NoPos }
// IsGenerated reports whether the file was generated by a program,
// not handwritten, by detecting the special comment described
// at https://go.dev/s/generatedcode.
//
// The syntax tree must have been parsed with the [parser.ParseComments] flag.
// Example:
//
// f, err := parser.ParseFile(fset, filename, src, parser.ParseComments|parser.PackageClauseOnly)
// if err != nil { ... }
// gen := ast.IsGenerated(f)
func ( *File) bool {
, := generator()
return
}
func generator( *File) (string, bool) {
for , := range .Comments {
for , := range .List {
if .Pos() > .Package {
break // after package declaration
}
// opt: check Contains first to avoid unnecessary array allocation in Split.
const = "// Code generated "
if strings.Contains(.Text, ) {
for , := range strings.Split(.Text, "\n") {
if , := strings.CutPrefix(, ); {
if , := strings.CutSuffix(, " DO NOT EDIT."); {
return , true
}
}
}
}
}
}
return "", false
}
// Unparen returns the expression with any enclosing parentheses removed.
func ( Expr) Expr {
for {
, := .(*ParenExpr)
if ! {
return
}
= .X
}
}
The pages are generated with Golds v0.7.0-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |