...

Source file src/github.com/bazelbuild/buildtools/build/print.go

Documentation: github.com/bazelbuild/buildtools/build

     1  /*
     2  Copyright 2016 Google LLC
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      https://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Printing of syntax trees.
    18  
    19  package build
    20  
    21  import (
    22  	"bytes"
    23  	"fmt"
    24  	"strings"
    25  
    26  	"github.com/bazelbuild/buildtools/tables"
    27  )
    28  
    29  const (
    30  	nestedIndentation = 4 // Indentation of nested blocks
    31  	listIndentation   = 4 // Indentation of multiline expressions
    32  	defIndentation    = 8 // Indentation of multiline function definitions
    33  )
    34  
    35  // FormatWithoutRewriting returns the formatted form of the given Starlark file.
    36  // This function is mostly useful for tests only, please consider using `Format` instead.
    37  func FormatWithoutRewriting(f *File) []byte {
    38  	pr := &printer{fileType: f.Type}
    39  	pr.file(f)
    40  	return pr.Bytes()
    41  }
    42  
    43  // Format rewrites the file and returns the formatted form of it.
    44  func Format(f *File) []byte {
    45  	Rewrite(f)
    46  	return FormatWithoutRewriting(f)
    47  }
    48  
    49  // FormatWithRewriter rewites the file with custom rewriter and returns the formatted form of it
    50  func FormatWithRewriter(w *Rewriter, f *File) []byte {
    51  	w.Rewrite(f)
    52  	return FormatWithoutRewriting(f)
    53  }
    54  
    55  // FormatString returns the string form of the given expression.
    56  func FormatString(x Expr) string {
    57  	// Expr is an interface and can be nil
    58  	if x == nil {
    59  		return ""
    60  	}
    61  
    62  	fileType := TypeBuild // for compatibility
    63  	if file, ok := x.(*File); ok {
    64  		fileType = file.Type
    65  	}
    66  
    67  	pr := &printer{fileType: fileType}
    68  	switch x := x.(type) {
    69  	case *File:
    70  		pr.file(x)
    71  	default:
    72  		pr.expr(x, precLow)
    73  	}
    74  	return pr.String()
    75  }
    76  
    77  // A printer collects the state during printing of a file or expression.
    78  type printer struct {
    79  	fileType     FileType  // different rules can be applied to different file types.
    80  	bytes.Buffer           // output buffer
    81  	comment      []Comment // pending end-of-line comments
    82  	margin       int       // left margin (indent), a number of spaces
    83  	depth        int       // nesting depth inside ( ) [ ] { }
    84  	level        int       // nesting level of def-, if-else- and for-blocks
    85  	needsNewLine bool      // true if the next statement needs a new line before it
    86  }
    87  
    88  // formattingMode returns the current file formatting mode.
    89  // Can be only TypeBuild or TypeDefault.
    90  func (p *printer) formattingMode() FileType {
    91  	switch p.fileType {
    92  	case TypeBuild, TypeWorkspace, TypeModule:
    93  		return TypeBuild
    94  	default: // TypeDefault, TypeBzl
    95  		return TypeDefault
    96  	}
    97  }
    98  
    99  // printf prints to the buffer.
   100  func (p *printer) printf(format string, args ...interface{}) {
   101  	fmt.Fprintf(p, format, args...)
   102  }
   103  
   104  // indent returns the position on the current line, in bytes, 0-indexed.
   105  func (p *printer) indent() int {
   106  	b := p.Bytes()
   107  	n := 0
   108  	for n < len(b) && b[len(b)-1-n] != '\n' {
   109  		n++
   110  	}
   111  	return n
   112  }
   113  
   114  // newline ends the current line, flushing end-of-line comments.
   115  // It must only be called when printing a newline is known to be safe:
   116  // when not inside an expression or when p.depth > 0.
   117  // To break a line inside an expression that might not be enclosed
   118  // in brackets of some kind, use breakline instead.
   119  func (p *printer) newline() {
   120  	p.needsNewLine = false
   121  	if len(p.comment) > 0 {
   122  		p.printf("  ")
   123  		for i, com := range p.comment {
   124  			if i > 0 {
   125  				p.trim()
   126  				p.printf("\n%*s", p.margin, "")
   127  			}
   128  			p.printf("%s", strings.TrimSpace(com.Token))
   129  		}
   130  		p.comment = p.comment[:0]
   131  	}
   132  
   133  	p.trim()
   134  	p.printf("\n%*s", p.margin, "")
   135  }
   136  
   137  // softNewline postpones a call to newline to the next call of p.newlineIfNeeded()
   138  // If softNewline is called several times, just one newline is printed.
   139  // Usecase: if there are several nested blocks ending at the same time, for instance
   140  //
   141  //	if True:
   142  //	    for a in b:
   143  //	        pass
   144  //	foo()
   145  //
   146  // the last statement (`pass`) doesn't end with a newline, each block ends with a lazy newline
   147  // which actually gets printed only once when right before the next statement (`foo()`) is printed.
   148  func (p *printer) softNewline() {
   149  	p.needsNewLine = true
   150  }
   151  
   152  // newlineIfNeeded calls newline if softNewline() has previously been called
   153  func (p *printer) newlineIfNeeded() {
   154  	if p.needsNewLine == true {
   155  		p.newline()
   156  	}
   157  }
   158  
   159  // breakline breaks the current line, inserting a continuation \ if needed.
   160  // If no continuation \ is needed, breakline flushes end-of-line comments.
   161  func (p *printer) breakline() {
   162  	if p.depth == 0 {
   163  		// Cannot have both final \ and comments.
   164  		p.printf(" \\\n%*s", p.margin, "")
   165  		return
   166  	}
   167  
   168  	// Safe to use newline.
   169  	p.newline()
   170  }
   171  
   172  // trim removes trailing spaces from the current line.
   173  func (p *printer) trim() {
   174  	// Remove trailing space from line we're about to end.
   175  	b := p.Bytes()
   176  	n := len(b)
   177  	for n > 0 && b[n-1] == ' ' {
   178  		n--
   179  	}
   180  	p.Truncate(n)
   181  }
   182  
   183  // file formats the given file into the print buffer.
   184  func (p *printer) file(f *File) {
   185  	for _, com := range f.Before {
   186  		p.printf("%s", strings.TrimSpace(com.Token))
   187  		p.newline()
   188  	}
   189  
   190  	p.statements(f.Stmt)
   191  
   192  	for _, com := range f.After {
   193  		p.printf("%s", strings.TrimSpace(com.Token))
   194  		p.newline()
   195  	}
   196  
   197  	p.newlineIfNeeded()
   198  }
   199  
   200  func (p *printer) nestedStatements(stmts []Expr) {
   201  	p.margin += nestedIndentation
   202  	p.level++
   203  	p.newline()
   204  
   205  	p.statements(stmts)
   206  
   207  	p.margin -= nestedIndentation
   208  	p.level--
   209  }
   210  
   211  func (p *printer) statements(rawStmts []Expr) {
   212  	// rawStmts may contain nils if a refactoring tool replaces an actual statement with nil.
   213  	// It means the statements don't exist anymore, just ignore them.
   214  
   215  	stmts := []Expr{}
   216  	for _, stmt := range rawStmts {
   217  		if stmt != nil {
   218  			stmts = append(stmts, stmt)
   219  		}
   220  	}
   221  
   222  	for i, stmt := range stmts {
   223  		p.expr(stmt, precLow)
   224  
   225  		// A CommentBlock is an empty statement without a body,
   226  		// it doesn't need an line break after the body
   227  		if _, ok := stmt.(*CommentBlock); !ok {
   228  			p.softNewline()
   229  		}
   230  
   231  		for _, com := range stmt.Comment().After {
   232  			p.newlineIfNeeded()
   233  			p.printf("%s", strings.TrimSpace(com.Token))
   234  			p.softNewline()
   235  		}
   236  
   237  		// Print an empty line break after the statement unless it's the last statement in the sequence.
   238  		// In that case a line break should be printed when the block or the file ends.
   239  		if i < len(stmts)-1 {
   240  			p.newline()
   241  		}
   242  
   243  		if i+1 < len(stmts) && !p.compactStmt(stmt, stmts[i+1]) {
   244  			p.newline()
   245  		}
   246  	}
   247  }
   248  
   249  // compactStmt reports whether the pair of statements s1, s2
   250  // should be printed without an intervening blank line.
   251  // We omit the blank line when both are subinclude statements
   252  // and the second one has no leading comments.
   253  func (p *printer) compactStmt(s1, s2 Expr) bool {
   254  	if len(s2.Comment().Before) > 0 || len(s1.Comment().After) > 0 {
   255  		return false
   256  	} else if isLoad(s1) && isLoad(s2) {
   257  		// Load statements should be compact
   258  		return true
   259  	} else if isLoad(s1) || isLoad(s2) {
   260  		// Load statements should be separated from anything else
   261  		return false
   262  	} else if p.fileType == TypeModule && areBazelDepsOfSameType(s1, s2) {
   263  		// bazel_dep statements in MODULE files should be compressed if they are both dev deps or
   264  		// both non-dev deps.
   265  		return true
   266  	} else if p.fileType == TypeModule && isBazelDepWithOverride(s1, s2) {
   267  		// Do not separate an override from the bazel_dep it overrides.
   268  		return true
   269  	} else if p.fileType == TypeModule && useSameModuleExtensionProxy(s1, s2) {
   270  		// Keep statements together that use the same module extension:
   271  		//
   272  		//   foo_deps = use_extension("//:foo.bzl", "foo_deps")
   273  		//   foo_deps.module(path = "github.com/foo/bar")
   274  		//   use_repo(foo_deps, "com_github_foo_bar")
   275  		return true
   276  	} else if isCommentBlock(s1) || isCommentBlock(s2) {
   277  		// Standalone comment blocks shouldn't be attached to other statements
   278  		return false
   279  	} else if (p.formattingMode() == TypeBuild) && p.level == 0 {
   280  		// Top-level statements in a BUILD or WORKSPACE file
   281  		return false
   282  	} else if isFunctionDefinition(s1) || isFunctionDefinition(s2) {
   283  		// On of the statements is a function definition
   284  		return false
   285  	} else {
   286  		// Depend on how the statements have been printed in the original file
   287  		_, end := s1.Span()
   288  		start, _ := s2.Span()
   289  		return start.Line-end.Line <= 1
   290  	}
   291  }
   292  
   293  // isLoad reports whether x is a load statement.
   294  func isLoad(x Expr) bool {
   295  	_, ok := x.(*LoadStmt)
   296  	return ok
   297  }
   298  
   299  // areBazelDepsOfSameType reports whether x and y are bazel_dep statements that
   300  // are both dev dependencies or both regular dependencies.
   301  func areBazelDepsOfSameType(x, y Expr) bool {
   302  	if !isBazelDep(x) || !isBazelDep(y) {
   303  		return false
   304  	}
   305  	isXDevDep := getKeywordBoolArgument(x.(*CallExpr), "dev_dependency", false)
   306  	isYDevDep := getKeywordBoolArgument(y.(*CallExpr), "dev_dependency", false)
   307  	return isXDevDep == isYDevDep
   308  }
   309  
   310  func isBazelDep(x Expr) bool {
   311  	call, ok := x.(*CallExpr)
   312  	if !ok {
   313  		return false
   314  	}
   315  	if ident, ok := call.X.(*Ident); ok && ident.Name == "bazel_dep" {
   316  		return true
   317  	}
   318  	return false
   319  }
   320  
   321  func isUseRepoOrUseExtension(x Expr) bool {
   322  	call, ok := x.(*CallExpr)
   323  	if !ok {
   324  		return false
   325  	}
   326  	if ident, ok := call.X.(*Ident); ok && (ident.Name == "use_repo" || ident.Name == "use_extension") {
   327  		return true
   328  	}
   329  	return false
   330  }
   331  
   332  func isModuleOverride(x Expr) bool {
   333  	call, ok := x.(*CallExpr)
   334  	if !ok {
   335  		return false
   336  	}
   337  	ident, ok := call.X.(*Ident)
   338  	if !ok {
   339  		return false
   340  	}
   341  	return tables.IsModuleOverride[ident.Name]
   342  }
   343  
   344  func getKeywordBoolArgument(call *CallExpr, keyword string, defaultValue bool) bool {
   345  	arg := getKeywordArgument(call, keyword)
   346  	if arg == nil {
   347  		return defaultValue
   348  	}
   349  	ident, ok := arg.(*Ident)
   350  	if !ok {
   351  		// Assume that the specified more complex value does not evaluate to the default.
   352  		return !defaultValue
   353  	}
   354  	return ident.Name == "True"
   355  }
   356  
   357  func getKeywordArgument(call *CallExpr, param string) Expr {
   358  	for _, arg := range call.List {
   359  		kwarg, ok := arg.(*AssignExpr)
   360  		if !ok {
   361  			continue
   362  		}
   363  		ident, ok := kwarg.LHS.(*Ident)
   364  		if !ok {
   365  			continue
   366  		}
   367  		if ident.Name == param {
   368  			return kwarg.RHS
   369  		}
   370  	}
   371  	return nil
   372  }
   373  
   374  func isBazelDepWithOverride(x, y Expr) bool {
   375  	if !isBazelDep(x) || !isModuleOverride(y) {
   376  		return false
   377  	}
   378  	bazelDepName, ok := getKeywordArgument(x.(*CallExpr), "name").(*StringExpr)
   379  	if !ok {
   380  		return false
   381  	}
   382  	overrideModuleName, ok := getKeywordArgument(y.(*CallExpr), "module_name").(*StringExpr)
   383  	if !ok {
   384  		return false
   385  	}
   386  	return bazelDepName.Value == overrideModuleName.Value
   387  }
   388  
   389  func useSameModuleExtensionProxy(x, y Expr) bool {
   390  	extX := usedModuleExtensionProxy(x)
   391  	if extX == "" {
   392  		return false
   393  	}
   394  	extY := usedModuleExtensionProxy(y)
   395  	return extX == extY
   396  }
   397  
   398  func usedModuleExtensionProxy(x Expr) string {
   399  	if call, ok := x.(*CallExpr); ok {
   400  		if callee, isIdent := call.X.(*Ident); isIdent && callee.Name == "use_repo" {
   401  			// Handles:
   402  			//   use_repo(foo_deps, "com_github_foo_bar")
   403  			if len(call.List) < 1 {
   404  				return ""
   405  			}
   406  			proxy, isIdent := call.List[0].(*Ident)
   407  			if !isIdent {
   408  				return ""
   409  			}
   410  			return proxy.Name
   411  		} else if dot, isDot := call.X.(*DotExpr); isDot {
   412  			// Handles:
   413  			//   foo_deps.module(path = "github.com/foo/bar")
   414  			extension, isIdent := dot.X.(*Ident)
   415  			if !isIdent {
   416  				return ""
   417  			}
   418  			return extension.Name
   419  		} else {
   420  			return ""
   421  		}
   422  	} else if assign, ok := x.(*AssignExpr); ok {
   423  		// Handles:
   424  		//   foo_deps = use_extension("//:foo.bzl", "foo_deps")
   425  		assignee, isIdent := assign.LHS.(*Ident)
   426  		if !isIdent {
   427  			return ""
   428  		}
   429  		call, isCall := assign.RHS.(*CallExpr)
   430  		if !isCall {
   431  			return ""
   432  		}
   433  		callee, isIdent := call.X.(*Ident)
   434  		if !isIdent || callee.Name != "use_extension" {
   435  			return ""
   436  		}
   437  		return assignee.Name
   438  	} else {
   439  		return ""
   440  	}
   441  }
   442  
   443  // isCommentBlock reports whether x is a comment block node.
   444  func isCommentBlock(x Expr) bool {
   445  	_, ok := x.(*CommentBlock)
   446  	return ok
   447  }
   448  
   449  // isFunctionDefinition checks if the statement is a def code block
   450  func isFunctionDefinition(x Expr) bool {
   451  	_, ok := x.(*DefStmt)
   452  	return ok
   453  }
   454  
   455  // isDifferentLines reports whether two positions belong to different lines.
   456  // If one of the positions is null (Line == 0), it's not a real position but probably an indicator
   457  // of manually inserted node. Return false in this case
   458  func isDifferentLines(p1, p2 *Position) bool {
   459  	if p1.Line == 0 || p2.Line == 0 {
   460  		return false
   461  	}
   462  	return p1.Line != p2.Line
   463  }
   464  
   465  // Expression formatting.
   466  
   467  // The expression formatter must introduce parentheses to force the
   468  // meaning described by the parse tree. We preserve parentheses in the
   469  // input, so extra parentheses are only needed if we have edited the tree.
   470  //
   471  // For example consider these expressions:
   472  //
   473  //	(1) "x" "y" % foo
   474  //	(2) "x" + "y" % foo
   475  //	(3) "x" + ("y" % foo)
   476  //	(4) ("x" + "y") % foo
   477  //
   478  // When we parse (1), we represent the concatenation as an addition.
   479  // However, if we print the addition back out without additional parens,
   480  // as in (2), it has the same meaning as (3), which is not the original
   481  // meaning. To preserve the original meaning we must add parens as in (4).
   482  //
   483  // To allow arbitrary rewrites to be formatted properly, we track full
   484  // operator precedence while printing instead of just handling this one
   485  // case of string concatenation.
   486  //
   487  // The precedences are assigned values low to high. A larger number
   488  // binds tighter than a smaller number. All binary operators bind
   489  // left-to-right.
   490  const (
   491  	precLow = iota
   492  	precAssign
   493  	precColon
   494  	precIfElse
   495  	precOr
   496  	precAnd
   497  	precCmp
   498  	precBitwiseOr
   499  	precBitwiseXor
   500  	precBitwiseAnd
   501  	precBitwiseShift
   502  	precAdd
   503  	precMultiply
   504  	precUnary
   505  	precSuffix
   506  )
   507  
   508  // opPrec gives the precedence for operators found in a BinaryExpr.
   509  var opPrec = map[string]int{
   510  	"or":     precOr,
   511  	"and":    precAnd,
   512  	"in":     precCmp,
   513  	"not in": precCmp,
   514  	"<":      precCmp,
   515  	">":      precCmp,
   516  	"==":     precCmp,
   517  	"!=":     precCmp,
   518  	"<=":     precCmp,
   519  	">=":     precCmp,
   520  	"+":      precAdd,
   521  	"-":      precAdd,
   522  	"*":      precMultiply,
   523  	"/":      precMultiply,
   524  	"//":     precMultiply,
   525  	"%":      precMultiply,
   526  	"|":      precBitwiseOr,
   527  	"&":      precBitwiseAnd,
   528  	"^":      precBitwiseXor,
   529  	"<<":     precBitwiseShift,
   530  	">>":     precBitwiseShift,
   531  }
   532  
   533  // expr prints the expression v to the print buffer.
   534  // The value outerPrec gives the precedence of the operator
   535  // outside expr. If that operator binds tighter than v's operator,
   536  // expr must introduce parentheses to preserve the meaning
   537  // of the parse tree (see above).
   538  func (p *printer) expr(v Expr, outerPrec int) {
   539  	// Emit line-comments preceding this expression.
   540  	// If we are in the middle of an expression but not inside ( ) [ ] { }
   541  	// then we cannot just break the line: we'd have to end it with a \.
   542  	// However, even then we can't emit line comments since that would
   543  	// end the expression. This is only a concern if we have rewritten
   544  	// the parse tree. If comments were okay before this expression in
   545  	// the original input they're still okay now, in the absence of rewrites.
   546  	//
   547  	// TODO(bazel-team): Check whether it is valid to emit comments right now,
   548  	// and if not, insert them earlier in the output instead, at the most
   549  	// recent \n not following a \ line.
   550  	p.newlineIfNeeded()
   551  
   552  	if before := v.Comment().Before; len(before) > 0 {
   553  		// Want to print a line comment.
   554  		// Line comments must be at the current margin.
   555  		p.trim()
   556  		if p.indent() > 0 {
   557  			// There's other text on the line. Start a new line.
   558  			p.printf("\n")
   559  		}
   560  		// Re-indent to margin.
   561  		p.printf("%*s", p.margin, "")
   562  		for _, com := range before {
   563  			p.printf("%s", strings.TrimSpace(com.Token))
   564  			p.newline()
   565  		}
   566  	}
   567  
   568  	// Do we introduce parentheses?
   569  	// The result depends on the kind of expression.
   570  	// Each expression type that might need parentheses
   571  	// calls addParen with its own precedence.
   572  	// If parentheses are necessary, addParen prints the
   573  	// opening parenthesis and sets parenthesized so that
   574  	// the code after the switch can print the closing one.
   575  	parenthesized := false
   576  	addParen := func(prec int) {
   577  		if prec < outerPrec {
   578  			p.printf("(")
   579  			p.depth++
   580  			parenthesized = true
   581  		}
   582  	}
   583  
   584  	switch v := v.(type) {
   585  	default:
   586  		panic(fmt.Errorf("printer: unexpected type %T", v))
   587  
   588  	case *CommentBlock:
   589  		// CommentBlock has no body
   590  
   591  	case *LiteralExpr:
   592  		p.printf("%s", v.Token)
   593  
   594  	case *Ident:
   595  		p.printf("%s", v.Name)
   596  
   597  	case *TypedIdent:
   598  		p.expr(v.Ident, precLow)
   599  		p.printf(": ")
   600  		p.expr(v.Type, precLow)
   601  
   602  	case *BranchStmt:
   603  		p.printf("%s", v.Token)
   604  
   605  	case *StringExpr:
   606  		// If the Token is a correct quoting of Value and has double quotes, use it,
   607  		// also use it if it has single quotes and the value itself contains a double quote symbol
   608  		// or if it's a raw string literal (starts with "r").
   609  		// This preserves the specific escaping choices that BUILD authors have made.
   610  		s, triple, err := Unquote(v.Token)
   611  		if err == nil && s == v.Value && triple == v.TripleQuote {
   612  			if strings.HasPrefix(v.Token, `r`) {
   613  				// Raw string literal
   614  				token := v.Token
   615  				if strings.HasSuffix(v.Token, `'`) && !strings.ContainsRune(v.Value, '"') {
   616  					// Single quotes but no double quotes inside the string, replace with double quotes
   617  					if strings.HasSuffix(token, `'''`) {
   618  						token = `r"""` + token[4:len(token)-3] + `"""`
   619  					} else if strings.HasSuffix(token, `'`) {
   620  						token = `r"` + token[2:len(token)-1] + `"`
   621  					}
   622  				}
   623  				p.printf("%s", token)
   624  				break
   625  			}
   626  
   627  			// Non-raw string literal
   628  			if strings.HasPrefix(v.Token, `"`) || strings.ContainsRune(v.Value, '"') {
   629  				// Either double quoted or there are double-quotes inside the string
   630  				if IsCorrectEscaping(v.Token) {
   631  					p.printf("%s", v.Token)
   632  					break
   633  				}
   634  			}
   635  		}
   636  
   637  		p.printf("%s", quote(v.Value, v.TripleQuote))
   638  
   639  	case *DotExpr:
   640  		addParen(precSuffix)
   641  		p.expr(v.X, precSuffix)
   642  		_, xEnd := v.X.Span()
   643  		isMultiline := isDifferentLines(&v.NamePos, &xEnd)
   644  		if isMultiline {
   645  			p.margin += listIndentation
   646  			p.breakline()
   647  		}
   648  		p.printf(".%s", v.Name)
   649  		if isMultiline {
   650  			p.margin -= listIndentation
   651  		}
   652  
   653  	case *IndexExpr:
   654  		addParen(precSuffix)
   655  		p.expr(v.X, precSuffix)
   656  		p.printf("[")
   657  		p.expr(v.Y, precLow)
   658  		p.printf("]")
   659  
   660  	case *KeyValueExpr:
   661  		p.expr(v.Key, precLow)
   662  		p.printf(": ")
   663  		p.expr(v.Value, precLow)
   664  
   665  	case *SliceExpr:
   666  		addParen(precSuffix)
   667  		p.expr(v.X, precSuffix)
   668  		p.printf("[")
   669  		if v.From != nil {
   670  			p.expr(v.From, precLow)
   671  		}
   672  		p.printf(":")
   673  		if v.To != nil {
   674  			p.expr(v.To, precLow)
   675  		}
   676  		if v.SecondColon.Byte != 0 {
   677  			p.printf(":")
   678  			if v.Step != nil {
   679  				p.expr(v.Step, precLow)
   680  			}
   681  		}
   682  		p.printf("]")
   683  
   684  	case *UnaryExpr:
   685  		addParen(precUnary)
   686  		if v.Op == "not" {
   687  			p.printf("not ") // Requires a space after it.
   688  		} else {
   689  			p.printf("%s", v.Op)
   690  		}
   691  		// Use the next precedence level (precSuffix), so that nested unary expressions are parenthesized,
   692  		// for example: `not (-(+(~foo)))` instead of `not -+~foo`
   693  		if v.X != nil {
   694  			p.expr(v.X, precSuffix)
   695  		}
   696  
   697  	case *LambdaExpr:
   698  		addParen(precColon)
   699  		p.printf("lambda")
   700  		for i, param := range v.Params {
   701  			if i > 0 {
   702  				p.printf(",")
   703  			}
   704  			p.printf(" ")
   705  			p.expr(param, precLow)
   706  		}
   707  		p.printf(": ")
   708  		p.expr(v.Body[0], precLow) // lambdas should have exactly one statement
   709  
   710  	case *BinaryExpr:
   711  		// Precedence: use the precedence of the operator.
   712  		// Since all binary expressions FormatWithoutRewriting left-to-right,
   713  		// it is okay for the left side to reuse the same operator
   714  		// without parentheses, so we use prec for v.X.
   715  		// For the same reason, the right side cannot reuse the same
   716  		// operator, or else a parse tree for a + (b + c), where the ( ) are
   717  		// not present in the source, will format as a + b + c, which
   718  		// means (a + b) + c. Treat the right expression as appearing
   719  		// in a context one precedence level higher: use prec+1 for v.Y.
   720  		//
   721  		// Line breaks: if we are to break the line immediately after
   722  		// the operator, introduce a margin at the current column,
   723  		// so that the second operand lines up with the first one and
   724  		// also so that neither operand can use space to the left.
   725  		// If the operator is an =, indent the right side another 4 spaces.
   726  		prec := opPrec[v.Op]
   727  		addParen(prec)
   728  		m := p.margin
   729  		if v.LineBreak {
   730  			p.margin = p.indent()
   731  		}
   732  
   733  		p.expr(v.X, prec)
   734  		p.printf(" %s", v.Op)
   735  		if v.LineBreak {
   736  			p.breakline()
   737  		} else {
   738  			p.printf(" ")
   739  		}
   740  		p.expr(v.Y, prec+1)
   741  		p.margin = m
   742  
   743  	case *AssignExpr:
   744  		addParen(precAssign)
   745  		m := p.margin
   746  		if v.LineBreak {
   747  			p.margin = p.indent() + listIndentation
   748  		}
   749  
   750  		p.expr(v.LHS, precAssign)
   751  		p.printf(" %s", v.Op)
   752  		if v.LineBreak {
   753  			p.breakline()
   754  		} else {
   755  			p.printf(" ")
   756  		}
   757  		p.expr(v.RHS, precAssign+1)
   758  		p.margin = m
   759  
   760  	case *ParenExpr:
   761  		p.seq("()", &v.Start, &[]Expr{v.X}, &v.End, modeParen, false, v.ForceMultiLine)
   762  
   763  	case *CallExpr:
   764  		forceCompact := v.ForceCompact
   765  		if p.fileType == TypeModule && (isBazelDep(v) || isUseRepoOrUseExtension(v)) {
   766  			start, end := v.Span()
   767  			forceCompact = start.Line == end.Line
   768  		}
   769  		addParen(precSuffix)
   770  		p.expr(v.X, precSuffix)
   771  		p.seq("()", &v.ListStart, &v.List, &v.End, modeCall, forceCompact, v.ForceMultiLine)
   772  
   773  	case *LoadStmt:
   774  		addParen(precSuffix)
   775  		p.printf("load")
   776  		args := []Expr{v.Module}
   777  		for i := range v.From {
   778  			from := v.From[i]
   779  			to := v.To[i]
   780  			var arg Expr
   781  			if from.Name == to.Name {
   782  				// Suffix comments are attached to the `to` token,
   783  				// Before comments are attached to the `from` token,
   784  				// they need to be combined.
   785  				arg = from.asString()
   786  				arg.Comment().Before = to.Comment().Before
   787  			} else {
   788  				arg = &AssignExpr{
   789  					LHS: to,
   790  					Op:  "=",
   791  					RHS: from.asString(),
   792  				}
   793  			}
   794  			args = append(args, arg)
   795  		}
   796  		p.seq("()", &v.Load, &args, &v.Rparen, modeLoad, v.ForceCompact, false)
   797  
   798  	case *ListExpr:
   799  		p.seq("[]", &v.Start, &v.List, &v.End, modeList, false, v.ForceMultiLine)
   800  
   801  	case *SetExpr:
   802  		p.seq("{}", &v.Start, &v.List, &v.End, modeList, false, v.ForceMultiLine)
   803  
   804  	case *TupleExpr:
   805  		mode := modeTuple
   806  		if v.NoBrackets {
   807  			mode = modeSeq
   808  		}
   809  		p.seq("()", &v.Start, &v.List, &v.End, mode, v.ForceCompact, v.ForceMultiLine)
   810  
   811  	case *DictExpr:
   812  		var list []Expr
   813  		for _, x := range v.List {
   814  			list = append(list, x)
   815  		}
   816  		p.seq("{}", &v.Start, &list, &v.End, modeDict, false, v.ForceMultiLine)
   817  
   818  	case *Comprehension:
   819  		p.listFor(v)
   820  
   821  	case *ConditionalExpr:
   822  		addParen(precSuffix)
   823  		p.expr(v.Then, precIfElse)
   824  		p.printf(" if ")
   825  		p.expr(v.Test, precIfElse)
   826  		p.printf(" else ")
   827  		p.expr(v.Else, precIfElse)
   828  
   829  	case *ReturnStmt:
   830  		p.printf("return")
   831  		if v.Result != nil {
   832  			p.printf(" ")
   833  			p.expr(v.Result, precLow)
   834  		}
   835  
   836  	case *DefStmt:
   837  		p.printf("def ")
   838  		p.printf(v.Name)
   839  		p.seq("()", &v.StartPos, &v.Params, nil, modeDef, v.ForceCompact, v.ForceMultiLine)
   840  		if v.Type != nil {
   841  			p.printf(" -> ")
   842  			p.expr(v.Type, precLow)
   843  		}
   844  		p.printf(":")
   845  		p.nestedStatements(v.Body)
   846  
   847  	case *ForStmt:
   848  		p.printf("for ")
   849  		p.expr(v.Vars, precLow)
   850  		p.printf(" in ")
   851  		p.expr(v.X, precLow)
   852  		p.printf(":")
   853  		p.nestedStatements(v.Body)
   854  
   855  	case *IfStmt:
   856  		block := v
   857  		isFirst := true
   858  		needsEmptyLine := false
   859  		for {
   860  			p.newlineIfNeeded()
   861  			if !isFirst {
   862  				if needsEmptyLine {
   863  					p.newline()
   864  				}
   865  				p.printf("el")
   866  			}
   867  			p.printf("if ")
   868  			p.expr(block.Cond, precLow)
   869  			p.printf(":")
   870  			p.nestedStatements(block.True)
   871  
   872  			isFirst = false
   873  			_, end := block.True[len(block.True)-1].Span()
   874  			needsEmptyLine = block.ElsePos.Pos.Line-end.Line > 1
   875  
   876  			// If the else-block contains just one statement which is an IfStmt, flatten it as a part
   877  			// of if-elif chain.
   878  			// Don't do it if the "else" statement has a suffix comment or if the next "if" statement
   879  			// has a before-comment.
   880  			if len(block.False) != 1 {
   881  				break
   882  			}
   883  			next, ok := block.False[0].(*IfStmt)
   884  			if !ok {
   885  				break
   886  			}
   887  			if len(block.ElsePos.Comment().Suffix) == 0 && len(next.Comment().Before) == 0 {
   888  				block = next
   889  				continue
   890  			}
   891  			break
   892  		}
   893  
   894  		if len(block.False) > 0 {
   895  			p.newlineIfNeeded()
   896  			if needsEmptyLine {
   897  				p.newline()
   898  			}
   899  			p.printf("else:")
   900  			p.comment = append(p.comment, block.ElsePos.Comment().Suffix...)
   901  			p.nestedStatements(block.False)
   902  		}
   903  	case *ForClause:
   904  		p.printf("for ")
   905  		p.expr(v.Vars, precLow)
   906  		p.printf(" in ")
   907  		p.expr(v.X, precLow)
   908  	case *IfClause:
   909  		p.printf("if ")
   910  		p.expr(v.Cond, precLow)
   911  	}
   912  
   913  	// Add closing parenthesis if needed.
   914  	if parenthesized {
   915  		p.depth--
   916  		p.printf(")")
   917  	}
   918  
   919  	// Queue end-of-line comments for printing when we
   920  	// reach the end of the line.
   921  	p.comment = append(p.comment, v.Comment().Suffix...)
   922  }
   923  
   924  // A seqMode describes a formatting mode for a sequence of values,
   925  // like a list or call arguments.
   926  type seqMode int
   927  
   928  const (
   929  	_ seqMode = iota
   930  
   931  	modeCall  // f(x)
   932  	modeList  // [x]
   933  	modeTuple // (x,)
   934  	modeParen // (x)
   935  	modeDict  // {x:y}
   936  	modeSeq   // x, y
   937  	modeDef   // def f(x, y)
   938  	modeLoad  // load(a, b, c)
   939  )
   940  
   941  // useCompactMode reports whether a sequence should be formatted in a compact mode
   942  func (p *printer) useCompactMode(start *Position, list *[]Expr, end *End, mode seqMode, forceCompact, forceMultiLine bool) bool {
   943  	// If there are line comments, use multiline
   944  	// so we can print the comments before the closing bracket.
   945  	for _, x := range *list {
   946  		if len(x.Comment().Before) > 0 || (len(x.Comment().Suffix) > 0 && mode != modeDef) {
   947  			return false
   948  		}
   949  	}
   950  	if end != nil && len(end.Before) > 0 {
   951  		return false
   952  	}
   953  
   954  	// Implicit tuples are always compact
   955  	if mode == modeSeq {
   956  		return true
   957  	}
   958  
   959  	// In the Default and .bzl printing modes try to keep the original printing style.
   960  	// Non-top-level statements and lists of arguments of a function definition
   961  	// should also keep the original style regardless of the mode.
   962  	if (p.level != 0 || p.formattingMode() == TypeDefault || mode == modeDef) && mode != modeLoad {
   963  		// If every element (including the brackets) ends on the same line where the next element starts,
   964  		// use the compact mode, otherwise use multiline mode.
   965  		// If an node's line number is 0, it means it doesn't appear in the original file,
   966  		// its position shouldn't be taken into account. Unless a sequence is new,
   967  		// then use multiline mode if ForceMultiLine mode was set.
   968  		previousEnd := start
   969  		isNewSeq := start.Line == 0
   970  		for _, x := range *list {
   971  			start, end := x.Span()
   972  			isNewSeq = isNewSeq && start.Line == 0
   973  			if isDifferentLines(&start, previousEnd) {
   974  				return false
   975  			}
   976  			if end.Line != 0 {
   977  				previousEnd = &end
   978  			}
   979  		}
   980  		if end != nil {
   981  			isNewSeq = isNewSeq && end.Pos.Line == 0
   982  			if isDifferentLines(previousEnd, &end.Pos) {
   983  				return false
   984  			}
   985  		}
   986  		if !isNewSeq {
   987  			return true
   988  		}
   989  		// Use the forceMultiline value for new sequences.
   990  		return !forceMultiLine
   991  	}
   992  	// In Build mode, use the forceMultiline and forceCompact values
   993  	if forceMultiLine {
   994  		return false
   995  	}
   996  	if forceCompact {
   997  		return true
   998  	}
   999  	// If neither of the flags are set, use compact mode only for empty or 1-element sequences
  1000  	return len(*list) <= 1
  1001  }
  1002  
  1003  // seq formats a list of values inside a given bracket pair (brack = "()", "[]", "{}").
  1004  // The end node holds any trailing comments to be printed just before the
  1005  // closing bracket.
  1006  // The mode parameter specifies the sequence mode (see above).
  1007  // If multiLine is true, seq avoids the compact form even
  1008  // for 0- and 1-element sequences.
  1009  func (p *printer) seq(brack string, start *Position, list *[]Expr, end *End, mode seqMode, forceCompact, forceMultiLine bool) {
  1010  	args := &[]Expr{}
  1011  	for _, x := range *list {
  1012  		// nil arguments may be added by some linter checks, filter them out because
  1013  		// they may cause NPE.
  1014  		if x != nil {
  1015  			*args = append(*args, x)
  1016  		}
  1017  	}
  1018  
  1019  	if mode != modeSeq {
  1020  		p.printf("%s", brack[:1])
  1021  	}
  1022  	p.depth++
  1023  	defer func() {
  1024  		p.depth--
  1025  		if mode != modeSeq {
  1026  			p.printf("%s", brack[1:])
  1027  		}
  1028  	}()
  1029  
  1030  	if p.useCompactMode(start, args, end, mode, forceCompact, forceMultiLine) {
  1031  		for i, x := range *args {
  1032  			if i > 0 {
  1033  				p.printf(", ")
  1034  			}
  1035  			p.expr(x, precLow)
  1036  		}
  1037  		// Single-element tuple must end with comma, to mark it as a tuple.
  1038  		if len(*args) == 1 && mode == modeTuple {
  1039  			p.printf(",")
  1040  		}
  1041  		return
  1042  	}
  1043  	// Multi-line form.
  1044  	indentation := listIndentation
  1045  	if mode == modeDef {
  1046  		indentation = defIndentation
  1047  	}
  1048  	p.margin += indentation
  1049  
  1050  	for i, x := range *args {
  1051  		// If we are about to break the line before the first
  1052  		// element and there are trailing end-of-line comments
  1053  		// waiting to be printed, delay them and print them as
  1054  		// whole-line comments preceding that element.
  1055  		// Do this by printing a newline ourselves and positioning
  1056  		// so that the end-of-line comment, with the two spaces added,
  1057  		// will line up with the current margin.
  1058  		if i == 0 && len(p.comment) > 0 {
  1059  			p.printf("\n%*s", p.margin-2, "")
  1060  		}
  1061  
  1062  		p.newline()
  1063  		p.expr(x, precLow)
  1064  
  1065  		if i+1 < len(*args) || needsTrailingComma(mode, x) {
  1066  			p.printf(",")
  1067  		}
  1068  	}
  1069  	// Final comments.
  1070  	if end != nil {
  1071  		for _, com := range end.Before {
  1072  			p.newline()
  1073  			p.printf("%s", strings.TrimSpace(com.Token))
  1074  		}
  1075  	}
  1076  	p.margin -= indentation
  1077  	// in modeDef print the closing bracket on the same line
  1078  	if mode != modeDef {
  1079  		p.newline()
  1080  	}
  1081  }
  1082  
  1083  func needsTrailingComma(mode seqMode, v Expr) bool {
  1084  	switch mode {
  1085  	case modeDef:
  1086  		return false
  1087  	case modeParen:
  1088  		return false
  1089  	case modeCall:
  1090  		// *args and **kwargs in fn calls
  1091  		switch v := v.(type) {
  1092  		case *UnaryExpr:
  1093  			if v.Op == "*" || v.Op == "**" {
  1094  				return false
  1095  			}
  1096  		}
  1097  	}
  1098  	return true
  1099  }
  1100  
  1101  // listFor formats a ListForExpr (list comprehension).
  1102  // The single-line form is:
  1103  //
  1104  //	[x for y in z if c]
  1105  //
  1106  // and the multi-line form is:
  1107  //
  1108  //	[
  1109  //	    x
  1110  //	    for y in z
  1111  //	    if c
  1112  //	]
  1113  func (p *printer) listFor(v *Comprehension) {
  1114  	multiLine := v.ForceMultiLine || len(v.End.Before) > 0
  1115  
  1116  	// space breaks the line in multiline mode
  1117  	// or else prints a space.
  1118  	space := func() {
  1119  		if multiLine {
  1120  			p.breakline()
  1121  		} else {
  1122  			p.printf(" ")
  1123  		}
  1124  	}
  1125  
  1126  	open, close := "[", "]"
  1127  	if v.Curly {
  1128  		open, close = "{", "}"
  1129  	}
  1130  	p.depth++
  1131  	p.printf("%s", open)
  1132  
  1133  	if multiLine {
  1134  		p.margin += listIndentation
  1135  		p.newline()
  1136  	}
  1137  
  1138  	p.expr(v.Body, precLow)
  1139  
  1140  	for _, c := range v.Clauses {
  1141  		space()
  1142  		p.expr(c, precLow)
  1143  	}
  1144  
  1145  	if multiLine {
  1146  		for _, com := range v.End.Before {
  1147  			p.newline()
  1148  			p.printf("%s", strings.TrimSpace(com.Token))
  1149  		}
  1150  		p.margin -= listIndentation
  1151  		p.newline()
  1152  	}
  1153  
  1154  	p.printf("%s", close)
  1155  	p.depth--
  1156  }
  1157  
  1158  func (p *printer) isTopLevel() bool {
  1159  	return p.margin == 0
  1160  }
  1161  

View as plain text