func()

in build/print.go [379:763]


func (p *printer) expr(v Expr, outerPrec int) {
	// Emit line-comments preceding this expression.
	// If we are in the middle of an expression but not inside ( ) [ ] { }
	// then we cannot just break the line: we'd have to end it with a \.
	// However, even then we can't emit line comments since that would
	// end the expression. This is only a concern if we have rewritten
	// the parse tree. If comments were okay before this expression in
	// the original input they're still okay now, in the absence of rewrites.
	//
	// TODO(bazel-team): Check whether it is valid to emit comments right now,
	// and if not, insert them earlier in the output instead, at the most
	// recent \n not following a \ line.
	p.newlineIfNeeded()

	if before := v.Comment().Before; len(before) > 0 {
		// Want to print a line comment.
		// Line comments must be at the current margin.
		p.trim()
		if p.indent() > 0 {
			// There's other text on the line. Start a new line.
			p.printf("\n")
		}
		// Re-indent to margin.
		p.printf("%*s", p.margin, "")
		for _, com := range before {
			p.printf("%s", strings.TrimSpace(com.Token))
			p.newline()
		}
	}

	// Do we introduce parentheses?
	// The result depends on the kind of expression.
	// Each expression type that might need parentheses
	// calls addParen with its own precedence.
	// If parentheses are necessary, addParen prints the
	// opening parenthesis and sets parenthesized so that
	// the code after the switch can print the closing one.
	parenthesized := false
	addParen := func(prec int) {
		if prec < outerPrec {
			p.printf("(")
			p.depth++
			parenthesized = true
		}
	}

	switch v := v.(type) {
	default:
		panic(fmt.Errorf("printer: unexpected type %T", v))

	case *CommentBlock:
		// CommentBlock has no body

	case *LiteralExpr:
		p.printf("%s", v.Token)

	case *Ident:
		p.printf("%s", v.Name)

	case *TypedIdent:
		p.expr(v.Ident, precLow)
		p.printf(": ")
		p.expr(v.Type, precLow)

	case *BranchStmt:
		p.printf("%s", v.Token)

	case *StringExpr:
		// If the Token is a correct quoting of Value and has double quotes, use it,
		// also use it if it has single quotes and the value itself contains a double quote symbol
		// or if it's a raw string literal (starts with "r").
		// This preserves the specific escaping choices that BUILD authors have made.
		s, triple, err := Unquote(v.Token)
		if err == nil && s == v.Value && triple == v.TripleQuote {
			if strings.HasPrefix(v.Token, `r`) {
				// Raw string literal
				token := v.Token
				if strings.HasSuffix(v.Token, `'`) && !strings.ContainsRune(v.Value, '"') {
					// Single quotes but no double quotes inside the string, replace with double quotes
					if strings.HasSuffix(token, `'''`) {
						token = `r"""` + token[4:len(token)-3] + `"""`
					} else if strings.HasSuffix(token, `'`) {
						token = `r"` + token[2:len(token)-1] + `"`
					}
				}
				p.printf("%s", token)
				break
			}

			// Non-raw string literal
			if strings.HasPrefix(v.Token, `"`) || strings.ContainsRune(v.Value, '"') {
				// Either double quoted or there are double-quotes inside the string
				if IsCorrectEscaping(v.Token) {
					p.printf("%s", v.Token)
					break
				}
			}
		}

		p.printf("%s", quote(v.Value, v.TripleQuote))

	case *DotExpr:
		addParen(precSuffix)
		p.expr(v.X, precSuffix)
		_, xEnd := v.X.Span()
		isMultiline := isDifferentLines(&v.NamePos, &xEnd)
		if isMultiline {
			p.margin += listIndentation
			p.breakline()
		}
		p.printf(".%s", v.Name)
		if isMultiline {
			p.margin -= listIndentation
		}

	case *IndexExpr:
		addParen(precSuffix)
		p.expr(v.X, precSuffix)
		p.printf("[")
		p.expr(v.Y, precLow)
		p.printf("]")

	case *KeyValueExpr:
		p.expr(v.Key, precLow)
		p.printf(": ")
		p.expr(v.Value, precLow)

	case *SliceExpr:
		addParen(precSuffix)
		p.expr(v.X, precSuffix)
		p.printf("[")
		if v.From != nil {
			p.expr(v.From, precLow)
		}
		p.printf(":")
		if v.To != nil {
			p.expr(v.To, precLow)
		}
		if v.SecondColon.Byte != 0 {
			p.printf(":")
			if v.Step != nil {
				p.expr(v.Step, precLow)
			}
		}
		p.printf("]")

	case *UnaryExpr:
		addParen(precUnary)
		if v.Op == "not" {
			p.printf("not ") // Requires a space after it.
		} else {
			p.printf("%s", v.Op)
		}
		// Use the next precedence level (precSuffix), so that nested unary expressions are parenthesized,
		// for example: `not (-(+(~foo)))` instead of `not -+~foo`
		if v.X != nil {
			p.expr(v.X, precSuffix)
		}

	case *LambdaExpr:
		addParen(precColon)
		p.printf("lambda")
		for i, param := range v.Params {
			if i > 0 {
				p.printf(",")
			}
			p.printf(" ")
			p.expr(param, precLow)
		}
		p.printf(": ")
		p.expr(v.Body[0], precLow) // lambdas should have exactly one statement

	case *BinaryExpr:
		// Precedence: use the precedence of the operator.
		// Since all binary expressions FormatWithoutRewriting left-to-right,
		// it is okay for the left side to reuse the same operator
		// without parentheses, so we use prec for v.X.
		// For the same reason, the right side cannot reuse the same
		// operator, or else a parse tree for a + (b + c), where the ( ) are
		// not present in the source, will format as a + b + c, which
		// means (a + b) + c. Treat the right expression as appearing
		// in a context one precedence level higher: use prec+1 for v.Y.
		//
		// Line breaks: if we are to break the line immediately after
		// the operator, introduce a margin at the current column,
		// so that the second operand lines up with the first one and
		// also so that neither operand can use space to the left.
		// If the operator is an =, indent the right side another 4 spaces.
		prec := opPrec[v.Op]
		addParen(prec)
		m := p.margin
		if v.LineBreak {
			p.margin = p.indent()
		}

		p.expr(v.X, prec)
		p.printf(" %s", v.Op)
		if v.LineBreak {
			p.breakline()
		} else {
			p.printf(" ")
		}
		p.expr(v.Y, prec+1)
		p.margin = m

	case *AssignExpr:
		addParen(precAssign)
		m := p.margin
		if v.LineBreak {
			p.margin = p.indent() + listIndentation
		}

		p.expr(v.LHS, precAssign)
		p.printf(" %s", v.Op)
		if v.LineBreak {
			p.breakline()
		} else {
			p.printf(" ")
		}
		p.expr(v.RHS, precAssign+1)
		p.margin = m

	case *ParenExpr:
		p.seq("()", &v.Start, &[]Expr{v.X}, &v.End, modeParen, false, v.ForceMultiLine)

	case *CallExpr:
		forceCompact := v.ForceCompact
		if p.fileType == TypeModule && isBazelDep(v) {
			start, end := v.Span()
			forceCompact = start.Line == end.Line
		}
		addParen(precSuffix)
		p.expr(v.X, precSuffix)
		p.seq("()", &v.ListStart, &v.List, &v.End, modeCall, forceCompact, v.ForceMultiLine)

	case *LoadStmt:
		addParen(precSuffix)
		p.printf("load")
		args := []Expr{v.Module}
		for i := range v.From {
			from := v.From[i]
			to := v.To[i]
			var arg Expr
			if from.Name == to.Name {
				// Suffix comments are attached to the `to` token,
				// Before comments are attached to the `from` token,
				// they need to be combined.
				arg = from.asString()
				arg.Comment().Before = to.Comment().Before
			} else {
				arg = &AssignExpr{
					LHS: to,
					Op:  "=",
					RHS: from.asString(),
				}
			}
			args = append(args, arg)
		}
		p.seq("()", &v.Load, &args, &v.Rparen, modeLoad, v.ForceCompact, false)

	case *ListExpr:
		p.seq("[]", &v.Start, &v.List, &v.End, modeList, false, v.ForceMultiLine)

	case *SetExpr:
		p.seq("{}", &v.Start, &v.List, &v.End, modeList, false, v.ForceMultiLine)

	case *TupleExpr:
		mode := modeTuple
		if v.NoBrackets {
			mode = modeSeq
		}
		p.seq("()", &v.Start, &v.List, &v.End, mode, v.ForceCompact, v.ForceMultiLine)

	case *DictExpr:
		var list []Expr
		for _, x := range v.List {
			list = append(list, x)
		}
		p.seq("{}", &v.Start, &list, &v.End, modeDict, false, v.ForceMultiLine)

	case *Comprehension:
		p.listFor(v)

	case *ConditionalExpr:
		addParen(precSuffix)
		p.expr(v.Then, precIfElse)
		p.printf(" if ")
		p.expr(v.Test, precIfElse)
		p.printf(" else ")
		p.expr(v.Else, precIfElse)

	case *ReturnStmt:
		p.printf("return")
		if v.Result != nil {
			p.printf(" ")
			p.expr(v.Result, precLow)
		}

	case *DefStmt:
		p.printf("def ")
		p.printf(v.Name)
		p.seq("()", &v.StartPos, &v.Params, nil, modeDef, v.ForceCompact, v.ForceMultiLine)
		if v.Type != nil {
			p.printf(" -> ")
			p.expr(v.Type, precLow)
		}
		p.printf(":")
		p.nestedStatements(v.Body)

	case *ForStmt:
		p.printf("for ")
		p.expr(v.Vars, precLow)
		p.printf(" in ")
		p.expr(v.X, precLow)
		p.printf(":")
		p.nestedStatements(v.Body)

	case *IfStmt:
		block := v
		isFirst := true
		needsEmptyLine := false
		for {
			p.newlineIfNeeded()
			if !isFirst {
				if needsEmptyLine {
					p.newline()
				}
				p.printf("el")
			}
			p.printf("if ")
			p.expr(block.Cond, precLow)
			p.printf(":")
			p.nestedStatements(block.True)

			isFirst = false
			_, end := block.True[len(block.True)-1].Span()
			needsEmptyLine = block.ElsePos.Pos.Line-end.Line > 1

			// If the else-block contains just one statement which is an IfStmt, flatten it as a part
			// of if-elif chain.
			// Don't do it if the "else" statement has a suffix comment or if the next "if" statement
			// has a before-comment.
			if len(block.False) != 1 {
				break
			}
			next, ok := block.False[0].(*IfStmt)
			if !ok {
				break
			}
			if len(block.ElsePos.Comment().Suffix) == 0 && len(next.Comment().Before) == 0 {
				block = next
				continue
			}
			break
		}

		if len(block.False) > 0 {
			p.newlineIfNeeded()
			if needsEmptyLine {
				p.newline()
			}
			p.printf("else:")
			p.comment = append(p.comment, block.ElsePos.Comment().Suffix...)
			p.nestedStatements(block.False)
		}
	case *ForClause:
		p.printf("for ")
		p.expr(v.Vars, precLow)
		p.printf(" in ")
		p.expr(v.X, precLow)
	case *IfClause:
		p.printf("if ")
		p.expr(v.Cond, precLow)
	}

	// Add closing parenthesis if needed.
	if parenthesized {
		p.depth--
		p.printf(")")
	}

	// Queue end-of-line comments for printing when we
	// reach the end of the line.
	p.comment = append(p.comment, v.Comment().Suffix...)
}