cuelang.org/go@v0.10.1/cue/format/node.go (about)

     1  // Copyright 2018 The CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package format
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  
    21  	"cuelang.org/go/cue/ast"
    22  	"cuelang.org/go/cue/literal"
    23  	"cuelang.org/go/cue/token"
    24  	"cuelang.org/go/internal"
    25  )
    26  
    27  func printNode(node interface{}, f *printer) error {
    28  	s := newFormatter(f)
    29  
    30  	ls := labelSimplifier{scope: map[string]bool{}}
    31  
    32  	// format node
    33  	f.allowed = nooverride // gobble initial whitespace.
    34  	switch x := node.(type) {
    35  	case *ast.File:
    36  		if f.cfg.simplify {
    37  			ls.markReferences(x)
    38  		}
    39  		s.file(x)
    40  	case ast.Expr:
    41  		if f.cfg.simplify {
    42  			ls.markReferences(x)
    43  		}
    44  		s.expr(x)
    45  	case ast.Decl:
    46  		if f.cfg.simplify {
    47  			ls.markReferences(x)
    48  		}
    49  		s.decl(x)
    50  	// case ast.Node: // TODO: do we need this?
    51  	// 	s.walk(x)
    52  	case []ast.Decl:
    53  		if f.cfg.simplify {
    54  			ls.processDecls(x)
    55  		}
    56  		s.walkDeclList(x)
    57  	default:
    58  		goto unsupported
    59  	}
    60  
    61  	return s.errs
    62  
    63  unsupported:
    64  	return fmt.Errorf("cue/format: unsupported node type %T", node)
    65  }
    66  
    67  func isRegularField(tok token.Token) bool {
    68  	return tok == token.ILLEGAL || tok == token.COLON
    69  }
    70  
    71  // Helper functions for common node lists. They may be empty.
    72  
    73  func nestDepth(f *ast.Field) int {
    74  	d := 1
    75  	if s, ok := f.Value.(*ast.StructLit); ok {
    76  		switch {
    77  		case len(s.Elts) != 1:
    78  			d = 0
    79  		default:
    80  			if f, ok := s.Elts[0].(*ast.Field); ok {
    81  				d += nestDepth(f)
    82  			}
    83  		}
    84  	}
    85  	return d
    86  }
    87  
    88  // TODO: be more accurate and move to astutil
    89  func hasDocComments(d ast.Decl) bool {
    90  	if len(ast.Comments(d)) > 0 {
    91  		return true
    92  	}
    93  	switch x := d.(type) {
    94  	case *ast.Field:
    95  		return len(ast.Comments(x.Label)) > 0
    96  	case *ast.Alias:
    97  		return len(ast.Comments(x.Ident)) > 0
    98  	case *ast.LetClause:
    99  		return len(ast.Comments(x.Ident)) > 0
   100  	}
   101  	return false
   102  }
   103  
   104  func (f *formatter) walkDeclList(list []ast.Decl) {
   105  	f.before(nil)
   106  	d := 0
   107  	var ellipsis ast.Decl
   108  	for i, x := range list {
   109  		if i > 0 {
   110  			f.print(declcomma)
   111  			nd := 0
   112  			if f, ok := x.(*ast.Field); ok {
   113  				nd = nestDepth(f)
   114  			}
   115  			if f.current.parentSep == newline && (d == 0 || nd != d) {
   116  				f.print(f.formfeed())
   117  			}
   118  			if hasDocComments(x) {
   119  				switch x := list[i-1].(type) {
   120  				case *ast.Field:
   121  					if internal.IsDefinition(x.Label) {
   122  						f.print(newsection)
   123  					}
   124  
   125  				default:
   126  					f.print(newsection)
   127  				}
   128  			}
   129  		}
   130  		if f.printer.cfg.simplify && internal.IsEllipsis(x) {
   131  			ellipsis = x
   132  			continue
   133  		}
   134  		f.decl(x)
   135  		d = 0
   136  		if f, ok := x.(*ast.Field); ok {
   137  			d = nestDepth(f)
   138  		}
   139  		if j := i + 1; j < len(list) {
   140  			switch x := list[j].(type) {
   141  			case *ast.Field:
   142  				switch x := x.Value.(type) {
   143  				case *ast.StructLit:
   144  					// TODO: not entirely correct: could have multiple elements,
   145  					// not have a valid Lbrace, and be marked multiline. This
   146  					// cannot occur for ASTs resulting from a parse, though.
   147  					if x.Lbrace.IsValid() || len(x.Elts) != 1 {
   148  						f.print(f.formfeed())
   149  						continue
   150  					}
   151  				case *ast.ListLit:
   152  					f.print(f.formfeed())
   153  					continue
   154  				}
   155  			}
   156  		}
   157  		f.print(f.current.parentSep)
   158  	}
   159  	if ellipsis != nil {
   160  		// ensure that comments associated with the original ellipsis are preserved
   161  		n := &ast.Ellipsis{}
   162  		ast.SetComments(n, ast.Comments(ellipsis))
   163  		f.decl(n)
   164  		f.print(f.current.parentSep)
   165  	}
   166  	f.after(nil)
   167  }
   168  
   169  func (f *formatter) walkSpecList(list []*ast.ImportSpec) {
   170  	f.before(nil)
   171  	for _, x := range list {
   172  		f.before(x)
   173  		f.importSpec(x)
   174  		f.after(x)
   175  	}
   176  	f.after(nil)
   177  }
   178  
   179  func (f *formatter) walkClauseList(list []ast.Clause, ws whiteSpace) {
   180  	f.before(nil)
   181  	for i, x := range list {
   182  		f.before(x)
   183  		// Only print the whitespace between the clauses.
   184  		if i > 0 {
   185  			f.print(ws)
   186  		}
   187  		f.clause(x)
   188  		f.after(x)
   189  	}
   190  	f.after(nil)
   191  }
   192  
   193  func (f *formatter) walkListElems(list []ast.Expr) {
   194  	f.before(nil)
   195  	for _, x := range list {
   196  		f.before(x)
   197  
   198  		// This is a hack to ensure that comments are printed correctly in lists.
   199  		// A comment must be printed after each element in a list, but we can't
   200  		// print a comma at the end of a comment because it will be considered
   201  		// part of the comment and ignored.
   202  		// To fix this we collect all comments that appear after the element,
   203  		// and only handle them after it's formatted.
   204  		var commentsAfter []*ast.CommentGroup
   205  		splitComments := x.Pos().IsValid()
   206  		if splitComments {
   207  			for _, cg := range ast.Comments(x) {
   208  				if x.Pos().Before(cg.Pos()) {
   209  					commentsAfter = append(commentsAfter, cg)
   210  				}
   211  			}
   212  		}
   213  
   214  		if splitComments {
   215  			f.current.cg = nil
   216  		}
   217  		switch n := x.(type) {
   218  		case *ast.Comprehension:
   219  			f.walkClauseList(n.Clauses, blank)
   220  			f.print(blank, nooverride)
   221  			f.expr(n.Value)
   222  
   223  		case *ast.Ellipsis:
   224  			f.ellipsis(n)
   225  
   226  		case *ast.Alias:
   227  			f.expr(n.Ident)
   228  			f.print(n.Equal, token.BIND)
   229  			f.expr(n.Expr)
   230  
   231  			// TODO: ast.CommentGroup: allows comment groups in ListLits.
   232  
   233  		case ast.Expr:
   234  			f.exprRaw(n, token.LowestPrec, 1)
   235  		}
   236  		f.print(comma, blank)
   237  
   238  		if splitComments {
   239  			f.current.cg = commentsAfter
   240  		}
   241  		f.after(x)
   242  	}
   243  	f.after(nil)
   244  }
   245  
   246  func (f *formatter) walkArgsList(list []ast.Expr, depth int) {
   247  	f.before(nil)
   248  	for _, x := range list {
   249  		f.before(x)
   250  		f.exprRaw(x, token.LowestPrec, depth)
   251  		f.print(comma, blank)
   252  		f.after(x)
   253  	}
   254  	f.after(nil)
   255  }
   256  
   257  func (f *formatter) file(file *ast.File) {
   258  	f.before(file)
   259  	f.walkDeclList(file.Decls)
   260  	f.after(file)
   261  	f.print(token.EOF)
   262  }
   263  
   264  func (f *formatter) inlineField(n *ast.Field) *ast.Field {
   265  	regular := internal.IsRegularField(n)
   266  	// shortcut single-element structs.
   267  	// If the label has a valid position, we assume that an unspecified
   268  	// Lbrace signals the intent to collapse fields.
   269  	if !n.Label.Pos().IsValid() && !(f.printer.cfg.simplify && regular) {
   270  		return nil
   271  	}
   272  
   273  	obj, ok := n.Value.(*ast.StructLit)
   274  	if !ok || len(obj.Elts) != 1 ||
   275  		(obj.Lbrace.IsValid() && !f.printer.cfg.simplify) ||
   276  		(obj.Lbrace.IsValid() && hasDocComments(n)) ||
   277  		len(n.Attrs) > 0 {
   278  		return nil
   279  	}
   280  
   281  	mem, ok := obj.Elts[0].(*ast.Field)
   282  	if !ok || len(mem.Attrs) > 0 {
   283  		return nil
   284  	}
   285  
   286  	if hasDocComments(mem) {
   287  		// TODO: this inserts curly braces even in spaces where this
   288  		// may not be desirable, such as:
   289  		// a:
   290  		//   // foo
   291  		//   b: 3
   292  		return nil
   293  	}
   294  	return mem
   295  }
   296  
   297  func (f *formatter) decl(decl ast.Decl) {
   298  	if decl == nil {
   299  		return
   300  	}
   301  	defer f.after(decl)
   302  	if !f.before(decl) {
   303  		return
   304  	}
   305  
   306  	switch n := decl.(type) {
   307  	case *ast.Field:
   308  		constraint, _ := internal.ConstraintToken(n)
   309  		f.label(n.Label, constraint)
   310  
   311  		regular := isRegularField(n.Token)
   312  		if regular {
   313  			f.print(noblank, nooverride, n.TokenPos, token.COLON)
   314  		} else {
   315  			f.print(blank, nooverride, n.Token)
   316  		}
   317  		f.visitComments(f.current.pos)
   318  
   319  		if mem := f.inlineField(n); mem != nil {
   320  			switch {
   321  			default:
   322  				fallthrough
   323  
   324  			case regular && f.cfg.simplify:
   325  				f.print(blank, nooverride)
   326  				f.decl(mem)
   327  
   328  			case mem.Label.Pos().IsNewline():
   329  				f.print(indent, formfeed)
   330  				f.decl(mem)
   331  				f.indent--
   332  			}
   333  			return
   334  		}
   335  
   336  		nextFF := f.nextNeedsFormfeed(n.Value)
   337  		tab := vtab
   338  		if nextFF || f.prevLbraceOnLine {
   339  			tab = blank
   340  		}
   341  
   342  		f.print(tab)
   343  
   344  		if n.Value != nil {
   345  			switch n.Value.(type) {
   346  			case *ast.ListLit, *ast.StructLit:
   347  				f.expr(n.Value)
   348  			default:
   349  				f.print(indent)
   350  				f.expr(n.Value)
   351  				f.markUnindentLine()
   352  			}
   353  		} else {
   354  			f.current.pos++
   355  			f.visitComments(f.current.pos)
   356  		}
   357  
   358  		space := tab
   359  		for _, a := range n.Attrs {
   360  			if f.before(a) {
   361  				f.print(space, a.At, a)
   362  			}
   363  			f.after(a)
   364  			space = blank
   365  		}
   366  
   367  		if nextFF {
   368  			f.print(formfeed)
   369  		}
   370  
   371  	case *ast.BadDecl:
   372  		f.print(n.From, "*bad decl*", declcomma)
   373  
   374  	case *ast.Package:
   375  		f.print(n.PackagePos, "package")
   376  		f.print(blank, n.Name, newsection, nooverride)
   377  
   378  	case *ast.ImportDecl:
   379  		f.print(n.Import, "import")
   380  		if len(n.Specs) == 0 {
   381  			f.print(blank, n.Lparen, token.LPAREN, n.Rparen, token.RPAREN, newline)
   382  			break
   383  		}
   384  		switch {
   385  		case len(n.Specs) == 1 && len(n.Specs[0].Comments()) == 0:
   386  			if !n.Lparen.IsValid() {
   387  				f.print(blank)
   388  				f.walkSpecList(n.Specs)
   389  				break
   390  			}
   391  			fallthrough
   392  		default:
   393  			f.print(blank, n.Lparen, token.LPAREN, newline, indent)
   394  			f.walkSpecList(n.Specs)
   395  			f.print(unindent, newline, n.Rparen, token.RPAREN, newline)
   396  		}
   397  		f.print(newsection, nooverride)
   398  
   399  	case *ast.LetClause:
   400  		if !decl.Pos().HasRelPos() || decl.Pos().RelPos() >= token.Newline {
   401  			f.print(formfeed)
   402  		}
   403  		f.print(n.Let, token.LET, blank, nooverride)
   404  		f.expr(n.Ident)
   405  		f.print(blank, nooverride, n.Equal, token.BIND, blank)
   406  		f.expr(n.Expr)
   407  		f.print(declcomma) // implied
   408  
   409  	case *ast.EmbedDecl:
   410  		if !n.Pos().HasRelPos() || n.Pos().RelPos() >= token.Newline {
   411  			f.print(formfeed)
   412  		}
   413  		f.expr(n.Expr)
   414  		f.print(newline)
   415  
   416  	case *ast.Attribute:
   417  		f.print(n.At, n)
   418  
   419  	case *ast.CommentGroup:
   420  		f.printComment(n)
   421  		f.print(newsection)
   422  
   423  	case ast.Expr:
   424  		f.embedding(n)
   425  	}
   426  }
   427  
   428  func (f *formatter) embedding(decl ast.Expr) {
   429  	switch n := decl.(type) {
   430  	case *ast.Comprehension:
   431  		if !n.Pos().HasRelPos() || n.Pos().RelPos() >= token.Newline {
   432  			f.print(formfeed)
   433  		}
   434  		f.walkClauseList(n.Clauses, blank)
   435  		f.print(blank, nooverride)
   436  		f.expr(n.Value)
   437  
   438  	case *ast.Ellipsis:
   439  		f.ellipsis(n)
   440  
   441  	case *ast.Alias:
   442  		if !decl.Pos().HasRelPos() || decl.Pos().RelPos() >= token.Newline {
   443  			f.print(formfeed)
   444  		}
   445  		f.expr(n.Ident)
   446  		f.print(blank, n.Equal, token.BIND, blank)
   447  		f.expr(n.Expr)
   448  		f.print(declcomma) // implied
   449  
   450  		// TODO: ast.CommentGroup: allows comment groups in ListLits.
   451  
   452  	case ast.Expr:
   453  		f.exprRaw(n, token.LowestPrec, 1)
   454  	}
   455  }
   456  
   457  func (f *formatter) nextNeedsFormfeed(n ast.Expr) bool {
   458  	switch x := n.(type) {
   459  	case *ast.StructLit:
   460  		return true
   461  	case *ast.BasicLit:
   462  		return strings.IndexByte(x.Value, '\n') >= 0
   463  	case *ast.ListLit:
   464  		return true
   465  	case *ast.ParenExpr:
   466  		return f.nextNeedsFormfeed(x.X)
   467  	case *ast.UnaryExpr:
   468  		return f.nextNeedsFormfeed(x.X)
   469  	case *ast.BinaryExpr:
   470  		return f.nextNeedsFormfeed(x.X) || f.nextNeedsFormfeed(x.Y)
   471  	case *ast.IndexExpr:
   472  		return f.nextNeedsFormfeed(x.X)
   473  	case *ast.SelectorExpr:
   474  		return f.nextNeedsFormfeed(x.X)
   475  	case *ast.CallExpr:
   476  		for _, arg := range x.Args {
   477  			if f.nextNeedsFormfeed(arg) {
   478  				return true
   479  			}
   480  		}
   481  	}
   482  	return false
   483  }
   484  
   485  func (f *formatter) importSpec(x *ast.ImportSpec) {
   486  	if x.Name != nil {
   487  		f.label(x.Name, token.ILLEGAL)
   488  		f.print(blank)
   489  	} else {
   490  		f.current.pos++
   491  		f.visitComments(f.current.pos)
   492  	}
   493  	f.expr(x.Path)
   494  	f.print(newline)
   495  }
   496  
   497  func (f *formatter) label(l ast.Label, constraint token.Token) {
   498  	f.before(l)
   499  	defer f.after(l)
   500  	switch n := l.(type) {
   501  	case *ast.Alias:
   502  		f.expr(n)
   503  
   504  	case *ast.Ident:
   505  		// Escape an identifier that has invalid characters. This may happen,
   506  		// if the AST is not generated by the parser.
   507  		name := n.Name
   508  		if !ast.IsValidIdent(name) {
   509  			name = literal.Label.Quote(n.Name)
   510  		}
   511  		f.print(n.NamePos, name)
   512  
   513  	case *ast.BasicLit:
   514  		str := n.Value
   515  		// Allow any CUE string in the AST, but ensure it is formatted
   516  		// according to spec.
   517  		if strings.HasPrefix(str, `"""`) || strings.HasPrefix(str, "#") {
   518  			if u, err := literal.Unquote(str); err == nil {
   519  				str = literal.Label.Quote(u)
   520  			}
   521  		}
   522  		f.print(n.ValuePos, str)
   523  
   524  	case *ast.ListLit:
   525  		f.expr(n)
   526  
   527  	case *ast.ParenExpr:
   528  		f.expr(n)
   529  
   530  	case *ast.Interpolation:
   531  		f.expr(n)
   532  
   533  	default:
   534  		panic(fmt.Sprintf("unknown label type %T", n))
   535  	}
   536  	if constraint != token.ILLEGAL {
   537  		f.print(constraint)
   538  	}
   539  }
   540  
   541  func (f *formatter) ellipsis(x *ast.Ellipsis) {
   542  	f.print(x.Ellipsis, token.ELLIPSIS)
   543  	if x.Type != nil && !isTop(x.Type) {
   544  		f.expr(x.Type)
   545  	}
   546  }
   547  
   548  func (f *formatter) expr(x ast.Expr) {
   549  	const depth = 1
   550  	f.expr1(x, token.LowestPrec, depth)
   551  }
   552  
   553  func (f *formatter) expr0(x ast.Expr, depth int) {
   554  	f.expr1(x, token.LowestPrec, depth)
   555  }
   556  
   557  func (f *formatter) expr1(expr ast.Expr, prec1, depth int) {
   558  	if f.before(expr) {
   559  		f.exprRaw(expr, prec1, depth)
   560  	}
   561  	f.after(expr)
   562  }
   563  
   564  func (f *formatter) exprRaw(expr ast.Expr, prec1, depth int) {
   565  
   566  	switch x := expr.(type) {
   567  	case *ast.BadExpr:
   568  		f.print(x.From, "_|_")
   569  
   570  	case *ast.BottomLit:
   571  		f.print(x.Bottom, token.BOTTOM)
   572  
   573  	case *ast.Alias:
   574  		// Aliases in expression positions are printed in short form.
   575  		f.label(x.Ident, token.ILLEGAL)
   576  		f.print(x.Equal, token.BIND)
   577  		f.expr(x.Expr)
   578  
   579  	case *ast.Ident:
   580  		f.print(x.NamePos, x)
   581  
   582  	case *ast.BinaryExpr:
   583  		if depth < 1 {
   584  			f.internalError("depth < 1:", depth)
   585  			depth = 1
   586  		}
   587  		f.binaryExpr(x, prec1, cutoff(x, depth), depth)
   588  
   589  	case *ast.UnaryExpr:
   590  		const prec = token.UnaryPrec
   591  		if prec < prec1 {
   592  			// parenthesis needed
   593  			f.print(token.LPAREN, nooverride)
   594  			f.expr(x)
   595  			f.print(token.RPAREN)
   596  		} else {
   597  			// no parenthesis needed
   598  			f.print(x.OpPos, x.Op, nooverride)
   599  			f.expr1(x.X, prec, depth)
   600  		}
   601  
   602  	case *ast.BasicLit:
   603  		f.print(x.ValuePos, x)
   604  
   605  	case *ast.Interpolation:
   606  		f.before(nil)
   607  		for _, x := range x.Elts {
   608  			f.expr0(x, depth+1)
   609  		}
   610  		f.after(nil)
   611  
   612  	case *ast.ParenExpr:
   613  		if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
   614  			// don't print parentheses around an already parenthesized expression
   615  			// TODO: consider making this more general and incorporate precedence levels
   616  			f.expr0(x.X, depth)
   617  		} else {
   618  			f.print(x.Lparen, token.LPAREN)
   619  			f.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth
   620  			f.print(x.Rparen, token.RPAREN)
   621  		}
   622  
   623  	case *ast.SelectorExpr:
   624  		f.selectorExpr(x, depth)
   625  
   626  	case *ast.IndexExpr:
   627  		f.expr1(x.X, token.HighestPrec, 1)
   628  		f.print(x.Lbrack, token.LBRACK)
   629  		f.expr0(x.Index, depth+1)
   630  		f.print(x.Rbrack, token.RBRACK)
   631  
   632  	case *ast.SliceExpr:
   633  		f.expr1(x.X, token.HighestPrec, 1)
   634  		f.print(x.Lbrack, token.LBRACK)
   635  		indices := []ast.Expr{x.Low, x.High}
   636  		for i, y := range indices {
   637  			if i > 0 {
   638  				// blanks around ":" if both sides exist and either side is a binary expression
   639  				x := indices[i-1]
   640  				if depth <= 1 && x != nil && y != nil && (isBinary(x) || isBinary(y)) {
   641  					f.print(blank, token.COLON, blank)
   642  				} else {
   643  					f.print(token.COLON)
   644  				}
   645  			}
   646  			if y != nil {
   647  				f.expr0(y, depth+1)
   648  			}
   649  		}
   650  		f.print(x.Rbrack, token.RBRACK)
   651  
   652  	case *ast.CallExpr:
   653  		if len(x.Args) > 1 {
   654  			depth++
   655  		}
   656  		wasIndented := f.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)
   657  		f.print(x.Lparen, token.LPAREN)
   658  		f.walkArgsList(x.Args, depth)
   659  		f.print(trailcomma, noblank, x.Rparen, token.RPAREN)
   660  		if wasIndented {
   661  			f.print(unindent)
   662  		}
   663  
   664  	case *ast.StructLit:
   665  		var l line
   666  		ws := noblank
   667  		ff := f.formfeed()
   668  
   669  		switch {
   670  		case len(x.Elts) == 0:
   671  			// collapse curly braces if the body is empty.
   672  			ffAlt := blank | nooverride
   673  			for _, c := range x.Comments() {
   674  				if c.Position == 1 {
   675  					ffAlt = ff
   676  					break
   677  				}
   678  			}
   679  			ff = ffAlt
   680  		case !x.Rbrace.HasRelPos() || !x.Elts[0].Pos().HasRelPos():
   681  			ws |= newline | nooverride
   682  		}
   683  		f.print(x.Lbrace, token.LBRACE, &l, ws, ff, indent)
   684  		f.prevLbraceOnLine = l == f.lineout
   685  
   686  		f.walkDeclList(x.Elts)
   687  		f.matchUnindent()
   688  
   689  		ws = noblank
   690  		if f.lineout != l {
   691  			ws |= newline
   692  			if f.lastTok != token.RBRACE && f.lastTok != token.RBRACK {
   693  				ws |= nooverride
   694  			}
   695  		}
   696  		f.print(ws, x.Rbrace, token.RBRACE)
   697  
   698  	case *ast.ListLit:
   699  		ws := noblank | indent
   700  		if len(x.Elts) == 0 {
   701  			// collapse square brackets if the body is empty.
   702  			collapseWs := blank | nooverride
   703  			for _, c := range x.Comments() {
   704  				if c.Position == 1 {
   705  					collapseWs = ws
   706  					break
   707  				}
   708  			}
   709  			ws |= collapseWs
   710  		}
   711  
   712  		f.print(x.Lbrack, token.LBRACK, ws)
   713  		f.walkListElems(x.Elts)
   714  		f.print(trailcomma, noblank)
   715  		f.visitComments(f.current.pos)
   716  		f.matchUnindent()
   717  		f.print(noblank, x.Rbrack, token.RBRACK)
   718  
   719  	case *ast.Ellipsis:
   720  		f.ellipsis(x)
   721  
   722  	default:
   723  		panic(fmt.Sprintf("unimplemented type %T", x))
   724  	}
   725  }
   726  
   727  func (f *formatter) clause(clause ast.Clause) {
   728  	switch n := clause.(type) {
   729  	case *ast.ForClause:
   730  		f.print(n.For, "for", blank)
   731  		f.print(indent)
   732  		if n.Key != nil {
   733  			f.label(n.Key, token.ILLEGAL)
   734  			f.print(n.Colon, token.COMMA, blank)
   735  		} else {
   736  			f.current.pos++
   737  			f.visitComments(f.current.pos)
   738  		}
   739  		f.label(n.Value, token.ILLEGAL)
   740  		f.print(blank, n.In, "in", blank)
   741  		f.expr(n.Source)
   742  		f.markUnindentLine()
   743  
   744  	case *ast.IfClause:
   745  		f.print(n.If, "if", blank)
   746  		f.print(indent)
   747  		f.expr(n.Condition)
   748  		f.markUnindentLine()
   749  
   750  	case *ast.LetClause:
   751  		// TODO(mvdan): LetClause is handled in both the clause and decl methods,
   752  		// because at the semantic level it is different in each case, but the code is repetitive.
   753  		f.print(n.Let, token.LET, blank, nooverride)
   754  		f.print(indent)
   755  		f.expr(n.Ident)
   756  		f.print(blank, nooverride, n.Equal, token.BIND, blank)
   757  		f.expr(n.Expr)
   758  		f.markUnindentLine()
   759  
   760  	default:
   761  		panic("unknown clause type")
   762  	}
   763  }
   764  
   765  func walkBinary(e *ast.BinaryExpr) (has6, has7, has8 bool, maxProblem int) {
   766  	switch e.Op.Precedence() {
   767  	case 6:
   768  		has6 = true
   769  	case 7:
   770  		has7 = true
   771  	case 8:
   772  		has8 = true
   773  	}
   774  
   775  	switch l := e.X.(type) {
   776  	case *ast.BinaryExpr:
   777  		if l.Op.Precedence() < e.Op.Precedence() {
   778  			// parens will be inserted.
   779  			// pretend this is an *syntax.ParenExpr and do nothing.
   780  			break
   781  		}
   782  		h6, h7, h8, mp := walkBinary(l)
   783  		has6 = has6 || h6
   784  		has7 = has7 || h7
   785  		has8 = has8 || h8
   786  		if maxProblem < mp {
   787  			maxProblem = mp
   788  		}
   789  	}
   790  
   791  	switch r := e.Y.(type) {
   792  	case *ast.BinaryExpr:
   793  		if r.Op.Precedence() <= e.Op.Precedence() {
   794  			// parens will be inserted.
   795  			// pretend this is an *syntax.ParenExpr and do nothing.
   796  			break
   797  		}
   798  		h6, h7, h8, mp := walkBinary(r)
   799  		has6 = has6 || h6
   800  		has7 = has7 || h7
   801  		has8 = has8 || h8
   802  		if maxProblem < mp {
   803  			maxProblem = mp
   804  		}
   805  
   806  	case *ast.UnaryExpr:
   807  		switch e.Op.String() + r.Op.String() {
   808  		case "/*":
   809  			maxProblem = 8
   810  		case "++", "--":
   811  			if maxProblem < 6 {
   812  				maxProblem = 6
   813  			}
   814  		}
   815  	}
   816  	return
   817  }
   818  
   819  func cutoff(e *ast.BinaryExpr, depth int) int {
   820  	has6, has7, has8, maxProblem := walkBinary(e)
   821  	if maxProblem > 0 {
   822  		return maxProblem + 1
   823  	}
   824  	if (has6 || has7) && has8 {
   825  		if depth == 1 {
   826  			return 8
   827  		}
   828  		if has7 {
   829  			return 7
   830  		}
   831  		return 6
   832  	}
   833  	if has6 && has7 {
   834  		if depth == 1 {
   835  			return 7
   836  		}
   837  		return 6
   838  	}
   839  	if depth == 1 {
   840  		return 8
   841  	}
   842  	return 6
   843  }
   844  
   845  func diffPrec(expr ast.Expr, prec int) int {
   846  	x, ok := expr.(*ast.BinaryExpr)
   847  	if !ok || prec != x.Op.Precedence() {
   848  		return 1
   849  	}
   850  	return 0
   851  }
   852  
   853  func reduceDepth(depth int) int {
   854  	depth--
   855  	if depth < 1 {
   856  		depth = 1
   857  	}
   858  	return depth
   859  }
   860  
   861  // Format the binary expression: decide the cutoff and then format.
   862  // Let's call depth == 1 Normal mode, and depth > 1 Compact mode.
   863  // (Algorithm suggestion by Russ Cox.)
   864  //
   865  // The precedences are:
   866  //
   867  //	7             *  /  % quo rem div mod
   868  //	6             +  -
   869  //	5             ==  !=  <  <=  >  >=
   870  //	4             &&
   871  //	3             ||
   872  //	2             &
   873  //	1             |
   874  //
   875  // The only decision is whether there will be spaces around levels 6 and 7.
   876  // There are never spaces at level 8 (unary), and always spaces at levels 5 and below.
   877  //
   878  // To choose the cutoff, look at the whole expression but excluding primary
   879  // expressions (function calls, parenthesized exprs), and apply these rules:
   880  //
   881  //  1. If there is a binary operator with a right side unary operand
   882  //     that would clash without a space, the cutoff must be (in order):
   883  //
   884  //     /*	8
   885  //     ++	7 // not necessary, but to avoid confusion
   886  //     --	7
   887  //
   888  //     (Comparison operators always have spaces around them.)
   889  //
   890  //  2. If there is a mix of level 7 and level 6 operators, then the cutoff
   891  //     is 7 (use spaces to distinguish precedence) in Normal mode
   892  //     and 6 (never use spaces) in Compact mode.
   893  //
   894  //  3. If there are no level 6 operators or no level 7 operators, then the
   895  //     cutoff is 8 (always use spaces) in Normal mode
   896  //     and 6 (never use spaces) in Compact mode.
   897  func (f *formatter) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) {
   898  	f.nestExpr++
   899  	defer func() { f.nestExpr-- }()
   900  
   901  	prec := x.Op.Precedence()
   902  	if prec < prec1 {
   903  		// parenthesis needed
   904  		// Note: The parser inserts a syntax.ParenExpr node; thus this case
   905  		//       can only occur if the AST is created in a different way.
   906  		// defer p.pushComment(nil).pop()
   907  		f.print(token.LPAREN, nooverride)
   908  		f.expr0(x, reduceDepth(depth)) // parentheses undo one level of depth
   909  		f.print(token.RPAREN)
   910  		return
   911  	}
   912  
   913  	printBlank := prec < cutoff
   914  
   915  	f.expr1(x.X, prec, depth+diffPrec(x.X, prec))
   916  	f.print(nooverride)
   917  	if printBlank {
   918  		f.print(blank)
   919  	}
   920  	f.print(x.OpPos, x.Op)
   921  	if x.Y.Pos().IsNewline() {
   922  		// at least one line break, but respect an extra empty line
   923  		// in the source
   924  		f.print(formfeed)
   925  		printBlank = false // no blank after line break
   926  	} else {
   927  		f.print(nooverride)
   928  	}
   929  	if printBlank {
   930  		f.print(blank)
   931  	}
   932  	f.expr1(x.Y, prec+1, depth+1)
   933  }
   934  
   935  func isBinary(expr ast.Expr) bool {
   936  	_, ok := expr.(*ast.BinaryExpr)
   937  	return ok
   938  }
   939  
   940  func (f *formatter) possibleSelectorExpr(expr ast.Expr, prec1, depth int) bool {
   941  	if x, ok := expr.(*ast.SelectorExpr); ok {
   942  		return f.selectorExpr(x, depth)
   943  	}
   944  	f.expr1(expr, prec1, depth)
   945  	return false
   946  }
   947  
   948  // selectorExpr handles an *syntax.SelectorExpr node and returns whether x spans
   949  // multiple lines.
   950  func (f *formatter) selectorExpr(x *ast.SelectorExpr, depth int) bool {
   951  	f.expr1(x.X, token.HighestPrec, depth)
   952  	f.print(token.PERIOD)
   953  	if x.Sel.Pos().IsNewline() {
   954  		f.print(indent, formfeed)
   955  		f.expr(x.Sel.(ast.Expr))
   956  		f.print(unindent)
   957  		return true
   958  	}
   959  	f.print(noblank)
   960  	f.expr(x.Sel.(ast.Expr))
   961  	return false
   962  }
   963  
   964  func isTop(e ast.Expr) bool {
   965  	ident, ok := e.(*ast.Ident)
   966  	return ok && ident.Name == "_"
   967  }