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