// 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. // // Note that for the CHAR and STRING kinds, the literal is stored // with its quotes. For example, for a double-quoted STRING, the // first and the last rune in the Value field will be ". The // [strconv.Unquote] and [strconv.UnquoteChar] functions can be // used to unquote STRING and CHAR values, respectively. // // For raw string literals (Kind == token.STRING && Value[0] == '`'), // the Value field contains the string text without carriage returns (\r) that // may have been present in the source. Because the end position is // computed using len(Value), the position reported by [BasicLit.End] does not match the // true source end position for raw string literals containing carriage returns. 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. // It may be invalid, for example in an empty file. // // (Use FileStart for the start of the entire file. It is always valid.) func ( *File) () token.Pos { return .Package } // End returns the end of the last declaration in the file. // It may be invalid, for example in an empty file. // // (Use FileEnd for the end of the entire file. It is always valid.) 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 } }