Functions
Fprint
function
#
Fprint "pretty-prints" an AST node to output.
It calls [Config.Fprint] with default settings.
Note that gofmt uses tabs for indentation but spaces for alignment;
use format.Node (package go/format) for output that matches gofmt.
func Fprint(output io.Writer, fset *token.FileSet, node any) error
Fprint
method
#
Fprint "pretty-prints" an AST node to output for a given configuration cfg.
Position information is interpreted relative to the file set fset.
The node type must be *[ast.File], *[CommentedNode], [][ast.Decl], [][ast.Stmt],
or assignment-compatible to [ast.Expr], [ast.Decl], [ast.Spec], or [ast.Stmt].
func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node any) error
Write
method
#
func (p *trimmer) Write(data []byte) (n int, err error)
Write
method
#
func (c *sizeCounter) Write(p []byte) (int, error)
allStars
function
#
allStars reports whether text is the interior of an
old-style /* */ comment with a star at the start of each line.
func allStars(text string) bool
appendLines
function
#
appendLines is like append(x, y...)
but it avoids creating doubled blank lines,
which would not be gofmt-standard output.
It assumes that only whole blocks of lines are being appended,
not line fragments.
func appendLines(x []byte, y []byte) []byte
binaryExpr
method
#
Format the binary expression: decide the cutoff and then format.
Let's call depth == 1 Normal mode, and depth > 1 Compact mode.
(Algorithm suggestion by Russ Cox.)
The precedences are:
5 * / % << >> & &^
4 + - | ^
3 == != < <= > >=
2 &&
1 ||
The only decision is whether there will be spaces around levels 4 and 5.
There are never spaces at level 6 (unary), and always spaces at levels 3 and below.
To choose the cutoff, look at the whole expression but excluding primary
expressions (function calls, parenthesized exprs), and apply these rules:
1. If there is a binary operator with a right side unary operand
that would clash without a space, the cutoff must be (in order):
/* 6
&& 6
&^ 6
++ 5
-- 5
(Comparison operators always have spaces around them.)
2. If there is a mix of level 5 and level 4 operators, then the cutoff
is 5 (use spaces to distinguish precedence) in Normal mode
and 4 (never use spaces) in Compact mode.
3. If there are no level 4 operators or no level 5 operators, then the
cutoff is 6 (always use spaces) in Normal mode
and 4 (never use spaces) in Compact mode.
func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1 int, cutoff int, depth int)
block
method
#
block prints an *ast.BlockStmt; it always spans at least two lines.
func (p *printer) block(b *ast.BlockStmt, nindent int)
bodySize
method
#
bodySize is like nodeSize but it is specialized for *ast.BlockStmt's.
func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int
combinesWithName
function
#
combinesWithName reports whether a name followed by the expression x
syntactically combines to another valid (value) expression. For instance
using *T for x, "name *T" syntactically appears as the expression x*T.
On the other hand, using P|Q or *P|~Q for x, "name P|Q" or "name *P|~Q"
cannot be combined into a valid (value) expression.
func combinesWithName(x ast.Expr) bool
commonPrefix
function
#
commonPrefix returns the common prefix of a and b.
func commonPrefix(a string, b string) string
containsLinebreak
method
#
containsLinebreak reports whether the whitespace buffer contains any line breaks.
func (p *printer) containsLinebreak() bool
controlClause
method
#
func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt)
cutoff
function
#
func cutoff(e *ast.BinaryExpr, depth int) int
decl
method
#
func (p *printer) decl(decl ast.Decl)
declList
method
#
func (p *printer) declList(list []ast.Decl)
declToken
function
#
func declToken(decl ast.Decl) (tok token.Token)
diffPrec
function
#
func diffPrec(expr ast.Expr, prec int) int
distanceFrom
method
#
distanceFrom returns the column difference between p.out (the current output
position) and startOutCol. If the start position is on a different line from
the current position (or either is unknown), the result is infinity.
func (p *printer) distanceFrom(startPos token.Pos, startOutCol int) int
expr
method
#
func (p *printer) expr(x ast.Expr)
expr0
method
#
func (p *printer) expr0(x ast.Expr, depth int)
expr1
method
#
func (p *printer) expr1(expr ast.Expr, prec1 int, depth int)
exprList
method
#
Print a list of expressions. If the list spans multiple
source lines, the original line breaks are respected between
expressions.
TODO(gri) Consider rewriting this to be independent of []ast.Expr
so that we can use the algorithm for any kind of list
(e.g., pass list via a channel over which to range).
func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos, isIncomplete bool)
fieldList
method
#
func (p *printer) fieldList(fields *ast.FieldList, isStruct bool, isIncomplete bool)
file
method
#
func (p *printer) file(src *ast.File)
fixGoBuildLines
method
#
func (p *printer) fixGoBuildLines()
flush
method
#
flush prints any pending comments and whitespace occurring textually
before the position of the next token tok. The flush result indicates
if a newline was written or if a formfeed was dropped from the whitespace
buffer.
func (p *printer) flush(next token.Position, tok token.Token) (wroteNewline bool, droppedFF bool)
fprint
method
#
fprint implements Fprint and takes a nodesSizes map for setting up the printer state.
func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node any, nodeSizes map[ast.Node]int) (err error)
free
method
#
func (p *printer) free()
funcBody
method
#
funcBody prints a function body following a function header of given headerSize.
If the header's and block's size are "small enough" and the block is "simple enough",
the block is printed on the current line, without line breaks, spaced from the header
by sep. Otherwise the block's opening "{" is printed on the current line, followed by
lines for the block's statements and its closing "}".
func (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt)
funcDecl
method
#
func (p *printer) funcDecl(d *ast.FuncDecl)
genDecl
method
#
func (p *printer) genDecl(d *ast.GenDecl)
getDoc
function
#
getDoc returns the ast.CommentGroup associated with n, if any.
func getDoc(n ast.Node) *ast.CommentGroup
identList
method
#
If indent is set, a multi-line identifier list is indented after the
first linebreak encountered.
func (p *printer) identList(list []*ast.Ident, indent bool)
identListSize
function
#
func identListSize(list []*ast.Ident, maxSize int) (size int)
indentList
method
#
indentList reports whether an expression list would look better if it
were indented wholesale (starting with the very first element, rather
than starting at the first line break).
func (p *printer) indentList(list []ast.Expr) bool
internalError
method
#
func (p *printer) internalError(msg ...any)
isBinary
function
#
func isBinary(expr ast.Expr) bool
isBlank
function
#
Returns true if s contains only white space
(only tabs and blanks can appear in the printer's context).
func isBlank(s string) bool
isDirective
function
#
isDirective reports whether c is a comment directive.
See go.dev/issue/37974.
This code is also in go/ast.
func isDirective(c string) bool
isNL
function
#
func isNL(b byte) bool
isOneLineFieldList
method
#
func (p *printer) isOneLineFieldList(list []*ast.Field) bool
isTypeElem
function
#
isTypeElem reports whether x is a (possibly parenthesized) type element expression.
The result is false if x could be a type element OR an ordinary (value) expression.
func isTypeElem(x ast.Expr) bool
isTypeName
function
#
func isTypeName(x ast.Expr) bool
keepTypeColumn
function
#
The keepTypeColumn function determines if the type column of a series of
consecutive const or var declarations must be kept, or if initialization
values (V) can be placed in the type column (T) instead. The i'th entry
in the result slice is true if the type column in spec[i] must be kept.
For example, the declaration:
const (
foobar int = 42 // comment
x = 7 // comment
foo
bar = 991
)
leads to the type/values matrix below. A run of value columns (V) can
be moved into the type column if there is no type for any of the values
in that column (we only move entire columns so that they align properly).
matrix formatted result
matrix
T V -> T V -> true there is a T and so the type
- V - V true column must be kept
- - - - false
- V V - false V is moved into T column
func keepTypeColumn(specs []ast.Spec) []bool
lineAt
method
#
func (p *printer) lineAt(start int) []byte
lineFor
method
#
func (p *printer) lineFor(pos token.Pos) int
linebreak
method
#
Print as many newlines as necessary (but at least min newlines) to get to
the current line. ws is printed before the first line break. If newSection
is set, the first line break is printed as formfeed. Returns 0 if no line
breaks were printed, returns 1 if there was exactly one newline printed,
and returns a value > 1 if there was a formfeed or more than one newline
printed.
TODO(gri): linebreak may add too many lines if the next statement at "line"
is preceded by comments because the computation of n assumes
the current position before the comment and the target position
after the comment. Thus, after interspersing such comments, the
space taken up by them is not considered to reduce the number of
linebreaks. At the moment there is no easy way to know about
future (not yet interspersed) comments in this function.
func (p *printer) linebreak(line int, min int, ws whiteSpace, newSection bool) (nbreaks int)
linesFrom
method
#
linesFrom returns the number of output lines between the current
output line and the line argument, ignoring any pending (not yet
emitted) whitespace or comments. It is used to compute an accurate
size (in number of lines) for a formatted construct.
func (p *printer) linesFrom(line int) int
mayCombine
function
#
func mayCombine(prev token.Token, next byte) (b bool)
newPrinter
function
#
func newPrinter(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) *printer
nlimit
function
#
nlimit limits n to maxNewlines.
func nlimit(n int) int
nodeSize
method
#
nodeSize determines the size of n in chars after formatting.
The result is <= maxSize if the node fits on one line with at
most maxSize chars and the formatted output doesn't contain
any control chars. Otherwise, the result is > maxSize.
func (p *printer) nodeSize(n ast.Node, maxSize int) (size int)
normalizedNumber
function
#
normalizedNumber rewrites base prefixes and exponents
of numbers to use lower-case letters (0X123 to 0x123 and 1.2E3 to 1.2e3),
and removes leading 0's from integer imaginary literals (0765i to 765i).
It leaves hexadecimal digits alone.
normalizedNumber doesn't modify the ast.BasicLit value lit points to.
If lit is not a number or a number in canonical format already,
lit is returned as is. Otherwise a new ast.BasicLit is created.
func normalizedNumber(lit *ast.BasicLit) *ast.BasicLit
numLines
method
#
numLines returns the number of lines spanned by node n in the original source.
func (p *printer) numLines(n ast.Node) int
parameters
method
#
func (p *printer) parameters(fields *ast.FieldList, mode paramMode)
posFor
method
#
func (p *printer) posFor(pos token.Pos) token.Position
possibleSelectorExpr
method
#
func (p *printer) possibleSelectorExpr(expr ast.Expr, prec1 int, depth int) bool
print
method
#
print prints a list of "items" (roughly corresponding to syntactic
tokens, but also including whitespace and formatting information).
It is the only print function that should be called directly from
any of the AST printing functions in nodes.go.
Whitespace is accumulated until a non-whitespace token appears. Any
comments that need to appear before that token are printed first,
taking into account the amount and structure of any pending white-
space for best comment placement. Then, any leftover whitespace is
printed, followed by the actual token.
func (p *printer) print(args ...any)
printNode
method
#
func (p *printer) printNode(node any) error
recordLine
method
#
recordLine records the output line number for the next non-whitespace
token in *linePtr. It is used to compute an accurate line number for a
formatted construct, independent of pending (not yet emitted) whitespace
or comments.
func (p *printer) recordLine(linePtr *int)
reduceDepth
function
#
func reduceDepth(depth int) int
resetSpace
method
#
func (p *trimmer) resetSpace()
sanitizeImportPath
function
#
func sanitizeImportPath(lit *ast.BasicLit) *ast.BasicLit
selectorExpr
method
#
selectorExpr handles an *ast.SelectorExpr node and reports whether x spans
multiple lines.
func (p *printer) selectorExpr(x *ast.SelectorExpr, depth int, isMethod bool) bool
setPos
method
#
func (p *printer) setPos(pos token.Pos)
signature
method
#
func (p *printer) signature(sig *ast.FuncType)
spec
method
#
The parameter n is the number of specs in the group. If doIndent is set,
multi-line identifier lists in the spec are indented when the first
linebreak is encountered.
func (p *printer) spec(spec ast.Spec, n int, doIndent bool)
stmt
method
#
func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool)
stmtList
method
#
Print the statement list indented, but without a newline after the last statement.
Extra line breaks between statements in the source are respected but at most one
empty line is printed between statements.
func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool)
stripCommonPrefix
function
#
stripCommonPrefix removes a common prefix from /*-style comment lines (unless no
comment line is indented, all but the first line have some form of space prefix).
The prefix is computed using heuristics such that is likely that the comment
contents are nicely laid out after re-printing each line using the printer's
current indentation.
func stripCommonPrefix(lines []string)
stripParens
function
#
func stripParens(x ast.Expr) ast.Expr
stripParensAlways
function
#
func stripParensAlways(x ast.Expr) ast.Expr
trimRight
function
#
trimRight returns s with trailing whitespace removed.
func trimRight(s string) string
valueSpec
method
#
func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool)
walkBinary
function
#
func walkBinary(e *ast.BinaryExpr) (has4 bool, has5 bool, maxProblem int)
writeByte
method
#
writeByte writes ch n times to p.output and updates p.pos.
Only used to write formatting (white space) characters.
func (p *printer) writeByte(ch byte, n int)
writeIndent
method
#
writeIndent writes indentation.
func (p *printer) writeIndent()
writeLineDirective
method
#
writeLineDirective writes a //line directive if necessary.
func (p *printer) writeLineDirective(pos token.Position)
writeString
method
#
writeString writes the string s to p.output and updates p.pos, p.out,
and p.last. If isLit is set, s is escaped w/ tabwriter.Escape characters
to protect s from being interpreted by the tabwriter.
Note: writeString is only used to write Go tokens, literals, and
comments, all of which must be written literally. Thus, it is correct
to always set isLit = true. However, setting it explicitly only when
needed (i.e., when we don't know that s contains no tabs or line breaks)
avoids processing extra escape characters and reduces run time of the
printer benchmark by up to 10%.
func (p *printer) writeString(pos token.Position, s string, isLit bool)
writeWhitespace
method
#
writeWhitespace writes the first n whitespace entries.
func (p *printer) writeWhitespace(n int)