Source file src/pkg/go/printer/nodes.go
1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // This file implements printing of AST nodes; specifically 6 // expressions, statements, declarations, and files. It uses 7 // the print functionality implemented in printer.go. 8 9 package printer 10 11 import ( 12 "bytes" 13 "go/ast" 14 "go/token" 15 "unicode/utf8" 16 ) 17 18 // Formatting issues: 19 // - better comment formatting for /*-style comments at the end of a line (e.g. a declaration) 20 // when the comment spans multiple lines; if such a comment is just two lines, formatting is 21 // not idempotent 22 // - formatting of expression lists 23 // - should use blank instead of tab to separate one-line function bodies from 24 // the function header unless there is a group of consecutive one-liners 25 26 // ---------------------------------------------------------------------------- 27 // Common AST nodes. 28 29 // Print as many newlines as necessary (but at least min newlines) to get to 30 // the current line. ws is printed before the first line break. If newSection 31 // is set, the first line break is printed as formfeed. Returns true if any 32 // line break was printed; returns false otherwise. 33 // 34 // TODO(gri): linebreak may add too many lines if the next statement at "line" 35 // is preceded by comments because the computation of n assumes 36 // the current position before the comment and the target position 37 // after the comment. Thus, after interspersing such comments, the 38 // space taken up by them is not considered to reduce the number of 39 // linebreaks. At the moment there is no easy way to know about 40 // future (not yet interspersed) comments in this function. 41 // 42 func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) { 43 n := nlimit(line - p.pos.Line) 44 if n < min { 45 n = min 46 } 47 if n > 0 { 48 p.print(ws) 49 if newSection { 50 p.print(formfeed) 51 n-- 52 } 53 for ; n > 0; n-- { 54 p.print(newline) 55 } 56 printedBreak = true 57 } 58 return 59 } 60 61 // setComment sets g as the next comment if g != nil and if node comments 62 // are enabled - this mode is used when printing source code fragments such 63 // as exports only. It assumes that there are no other pending comments to 64 // intersperse. 65 func (p *printer) setComment(g *ast.CommentGroup) { 66 if g == nil || !p.useNodeComments { 67 return 68 } 69 if p.comments == nil { 70 // initialize p.comments lazily 71 p.comments = make([]*ast.CommentGroup, 1) 72 } else if p.cindex < len(p.comments) { 73 // for some reason there are pending comments; this 74 // should never happen - handle gracefully and flush 75 // all comments up to g, ignore anything after that 76 p.flush(p.posFor(g.List[0].Pos()), token.ILLEGAL) 77 } 78 p.comments[0] = g 79 p.cindex = 0 80 p.nextComment() // get comment ready for use 81 } 82 83 type exprListMode uint 84 85 const ( 86 commaTerm exprListMode = 1 << iota // list is optionally terminated by a comma 87 noIndent // no extra indentation in multi-line lists 88 ) 89 90 // If indent is set, a multi-line identifier list is indented after the 91 // first linebreak encountered. 92 func (p *printer) identList(list []*ast.Ident, indent bool) { 93 // convert into an expression list so we can re-use exprList formatting 94 xlist := make([]ast.Expr, len(list)) 95 for i, x := range list { 96 xlist[i] = x 97 } 98 var mode exprListMode 99 if !indent { 100 mode = noIndent 101 } 102 p.exprList(token.NoPos, xlist, 1, mode, token.NoPos) 103 } 104 105 // Print a list of expressions. If the list spans multiple 106 // source lines, the original line breaks are respected between 107 // expressions. 108 // 109 // TODO(gri) Consider rewriting this to be independent of []ast.Expr 110 // so that we can use the algorithm for any kind of list 111 // (e.g., pass list via a channel over which to range). 112 func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos) { 113 if len(list) == 0 { 114 return 115 } 116 117 prev := p.posFor(prev0) 118 next := p.posFor(next0) 119 line := p.lineFor(list[0].Pos()) 120 endLine := p.lineFor(list[len(list)-1].End()) 121 122 if prev.IsValid() && prev.Line == line && line == endLine { 123 // all list entries on a single line 124 for i, x := range list { 125 if i > 0 { 126 // use position of expression following the comma as 127 // comma position for correct comment placement 128 p.print(x.Pos(), token.COMMA, blank) 129 } 130 p.expr0(x, depth) 131 } 132 return 133 } 134 135 // list entries span multiple lines; 136 // use source code positions to guide line breaks 137 138 // don't add extra indentation if noIndent is set; 139 // i.e., pretend that the first line is already indented 140 ws := ignore 141 if mode&noIndent == 0 { 142 ws = indent 143 } 144 145 // the first linebreak is always a formfeed since this section must not 146 // depend on any previous formatting 147 prevBreak := -1 // index of last expression that was followed by a linebreak 148 if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) { 149 ws = ignore 150 prevBreak = 0 151 } 152 153 // initialize expression/key size: a zero value indicates expr/key doesn't fit on a single line 154 size := 0 155 156 // print all list elements 157 for i, x := range list { 158 prevLine := line 159 line = p.lineFor(x.Pos()) 160 161 // determine if the next linebreak, if any, needs to use formfeed: 162 // in general, use the entire node size to make the decision; for 163 // key:value expressions, use the key size 164 // TODO(gri) for a better result, should probably incorporate both 165 // the key and the node size into the decision process 166 useFF := true 167 168 // determine element size: all bets are off if we don't have 169 // position information for the previous and next token (likely 170 // generated code - simply ignore the size in this case by setting 171 // it to 0) 172 prevSize := size 173 const infinity = 1e6 // larger than any source line 174 size = p.nodeSize(x, infinity) 175 pair, isPair := x.(*ast.KeyValueExpr) 176 if size <= infinity && prev.IsValid() && next.IsValid() { 177 // x fits on a single line 178 if isPair { 179 size = p.nodeSize(pair.Key, infinity) // size <= infinity 180 } 181 } else { 182 // size too large or we don't have good layout information 183 size = 0 184 } 185 186 // if the previous line and the current line had single- 187 // line-expressions and the key sizes are small or the 188 // the ratio between the key sizes does not exceed a 189 // threshold, align columns and do not use formfeed 190 if prevSize > 0 && size > 0 { 191 const smallSize = 20 192 if prevSize <= smallSize && size <= smallSize { 193 useFF = false 194 } else { 195 const r = 4 // threshold 196 ratio := float64(size) / float64(prevSize) 197 useFF = ratio <= 1/r || r <= ratio 198 } 199 } 200 201 if i > 0 { 202 needsLinebreak := prevLine < line && prevLine > 0 && line > 0 203 // use position of expression following the comma as 204 // comma position for correct comment placement, but 205 // only if the expression is on the same line 206 if !needsLinebreak { 207 p.print(x.Pos()) 208 } 209 p.print(token.COMMA) 210 needsBlank := true 211 if needsLinebreak { 212 // lines are broken using newlines so comments remain aligned 213 // unless forceFF is set or there are multiple expressions on 214 // the same line in which case formfeed is used 215 if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) { 216 ws = ignore 217 prevBreak = i 218 needsBlank = false // we got a line break instead 219 } 220 } 221 if needsBlank { 222 p.print(blank) 223 } 224 } 225 226 if isPair && size > 0 && len(list) > 1 { 227 // we have a key:value expression that fits onto one line and 228 // is in a list with more then one entry: use a column for the 229 // key such that consecutive entries can align if possible 230 p.expr(pair.Key) 231 p.print(pair.Colon, token.COLON, vtab) 232 p.expr(pair.Value) 233 } else { 234 p.expr0(x, depth) 235 } 236 } 237 238 if mode&commaTerm != 0 && next.IsValid() && p.pos.Line < next.Line { 239 // print a terminating comma if the next token is on a new line 240 p.print(token.COMMA) 241 if ws == ignore && mode&noIndent == 0 { 242 // unindent if we indented 243 p.print(unindent) 244 } 245 p.print(formfeed) // terminating comma needs a line break to look good 246 return 247 } 248 249 if ws == ignore && mode&noIndent == 0 { 250 // unindent if we indented 251 p.print(unindent) 252 } 253 } 254 255 func (p *printer) parameters(fields *ast.FieldList) { 256 p.print(fields.Opening, token.LPAREN) 257 if len(fields.List) > 0 { 258 prevLine := p.lineFor(fields.Opening) 259 ws := indent 260 for i, par := range fields.List { 261 // determine par begin and end line (may be different 262 // if there are multiple parameter names for this par 263 // or the type is on a separate line) 264 var parLineBeg int 265 var parLineEnd = p.lineFor(par.Type.Pos()) 266 if len(par.Names) > 0 { 267 parLineBeg = p.lineFor(par.Names[0].Pos()) 268 } else { 269 parLineBeg = parLineEnd 270 } 271 // separating "," if needed 272 needsLinebreak := 0 < prevLine && prevLine < parLineBeg 273 if i > 0 { 274 // use position of parameter following the comma as 275 // comma position for correct comma placement, but 276 // only if the next parameter is on the same line 277 if !needsLinebreak { 278 p.print(par.Pos()) 279 } 280 p.print(token.COMMA) 281 } 282 // separator if needed (linebreak or blank) 283 if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) { 284 // break line if the opening "(" or previous parameter ended on a different line 285 ws = ignore 286 } else if i > 0 { 287 p.print(blank) 288 } 289 // parameter names 290 if len(par.Names) > 0 { 291 // Very subtle: If we indented before (ws == ignore), identList 292 // won't indent again. If we didn't (ws == indent), identList will 293 // indent if the identList spans multiple lines, and it will outdent 294 // again at the end (and still ws == indent). Thus, a subsequent indent 295 // by a linebreak call after a type, or in the next multi-line identList 296 // will do the right thing. 297 p.identList(par.Names, ws == indent) 298 p.print(blank) 299 } 300 // parameter type 301 p.expr(par.Type) 302 prevLine = parLineEnd 303 } 304 // if the closing ")" is on a separate line from the last parameter, 305 // print an additional "," and line break 306 if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing { 307 p.print(token.COMMA) 308 p.linebreak(closing, 0, ignore, true) 309 } 310 // unindent if we indented 311 if ws == ignore { 312 p.print(unindent) 313 } 314 } 315 p.print(fields.Closing, token.RPAREN) 316 } 317 318 func (p *printer) signature(params, result *ast.FieldList) { 319 p.parameters(params) 320 n := result.NumFields() 321 if n > 0 { 322 p.print(blank) 323 if n == 1 && result.List[0].Names == nil { 324 // single anonymous result; no ()'s 325 p.expr(result.List[0].Type) 326 return 327 } 328 p.parameters(result) 329 } 330 } 331 332 func identListSize(list []*ast.Ident, maxSize int) (size int) { 333 for i, x := range list { 334 if i > 0 { 335 size += len(", ") 336 } 337 size += utf8.RuneCountInString(x.Name) 338 if size >= maxSize { 339 break 340 } 341 } 342 return 343 } 344 345 func (p *printer) isOneLineFieldList(list []*ast.Field) bool { 346 if len(list) != 1 { 347 return false // allow only one field 348 } 349 f := list[0] 350 if f.Tag != nil || f.Comment != nil { 351 return false // don't allow tags or comments 352 } 353 // only name(s) and type 354 const maxSize = 30 // adjust as appropriate, this is an approximate value 355 namesSize := identListSize(f.Names, maxSize) 356 if namesSize > 0 { 357 namesSize = 1 // blank between names and types 358 } 359 typeSize := p.nodeSize(f.Type, maxSize) 360 return namesSize+typeSize <= maxSize 361 } 362 363 func (p *printer) setLineComment(text string) { 364 p.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}}) 365 } 366 367 func (p *printer) isMultiLine(n ast.Node) bool { 368 return p.lineFor(n.End())-p.lineFor(n.Pos()) > 0 369 } 370 371 func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) { 372 lbrace := fields.Opening 373 list := fields.List 374 rbrace := fields.Closing 375 hasComments := isIncomplete || p.commentBefore(p.posFor(rbrace)) 376 srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.lineFor(lbrace) == p.lineFor(rbrace) 377 378 if !hasComments && srcIsOneLine { 379 // possibly a one-line struct/interface 380 if len(list) == 0 { 381 // no blank between keyword and {} in this case 382 p.print(lbrace, token.LBRACE, rbrace, token.RBRACE) 383 return 384 } else if isStruct && p.isOneLineFieldList(list) { // for now ignore interfaces 385 // small enough - print on one line 386 // (don't use identList and ignore source line breaks) 387 p.print(lbrace, token.LBRACE, blank) 388 f := list[0] 389 for i, x := range f.Names { 390 if i > 0 { 391 // no comments so no need for comma position 392 p.print(token.COMMA, blank) 393 } 394 p.expr(x) 395 } 396 if len(f.Names) > 0 { 397 p.print(blank) 398 } 399 p.expr(f.Type) 400 p.print(blank, rbrace, token.RBRACE) 401 return 402 } 403 } 404 // hasComments || !srcIsOneLine 405 406 p.print(blank, lbrace, token.LBRACE, indent) 407 if hasComments || len(list) > 0 { 408 p.print(formfeed) 409 } 410 411 if isStruct { 412 413 sep := vtab 414 if len(list) == 1 { 415 sep = blank 416 } 417 newSection := false 418 for i, f := range list { 419 if i > 0 { 420 p.linebreak(p.lineFor(f.Pos()), 1, ignore, newSection) 421 } 422 extraTabs := 0 423 p.setComment(f.Doc) 424 if len(f.Names) > 0 { 425 // named fields 426 p.identList(f.Names, false) 427 p.print(sep) 428 p.expr(f.Type) 429 extraTabs = 1 430 } else { 431 // anonymous field 432 p.expr(f.Type) 433 extraTabs = 2 434 } 435 if f.Tag != nil { 436 if len(f.Names) > 0 && sep == vtab { 437 p.print(sep) 438 } 439 p.print(sep) 440 p.expr(f.Tag) 441 extraTabs = 0 442 } 443 if f.Comment != nil { 444 for ; extraTabs > 0; extraTabs-- { 445 p.print(sep) 446 } 447 p.setComment(f.Comment) 448 } 449 newSection = p.isMultiLine(f) 450 } 451 if isIncomplete { 452 if len(list) > 0 { 453 p.print(formfeed) 454 } 455 p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment 456 p.setLineComment("// contains filtered or unexported fields") 457 } 458 459 } else { // interface 460 461 newSection := false 462 for i, f := range list { 463 if i > 0 { 464 p.linebreak(p.lineFor(f.Pos()), 1, ignore, newSection) 465 } 466 p.setComment(f.Doc) 467 if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp { 468 // method 469 p.expr(f.Names[0]) 470 p.signature(ftyp.Params, ftyp.Results) 471 } else { 472 // embedded interface 473 p.expr(f.Type) 474 } 475 p.setComment(f.Comment) 476 newSection = p.isMultiLine(f) 477 } 478 if isIncomplete { 479 if len(list) > 0 { 480 p.print(formfeed) 481 } 482 p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment 483 p.setLineComment("// contains filtered or unexported methods") 484 } 485 486 } 487 p.print(unindent, formfeed, rbrace, token.RBRACE) 488 } 489 490 // ---------------------------------------------------------------------------- 491 // Expressions 492 493 func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) { 494 switch e.Op.Precedence() { 495 case 4: 496 has4 = true 497 case 5: 498 has5 = true 499 } 500 501 switch l := e.X.(type) { 502 case *ast.BinaryExpr: 503 if l.Op.Precedence() < e.Op.Precedence() { 504 // parens will be inserted. 505 // pretend this is an *ast.ParenExpr and do nothing. 506 break 507 } 508 h4, h5, mp := walkBinary(l) 509 has4 = has4 || h4 510 has5 = has5 || h5 511 if maxProblem < mp { 512 maxProblem = mp 513 } 514 } 515 516 switch r := e.Y.(type) { 517 case *ast.BinaryExpr: 518 if r.Op.Precedence() <= e.Op.Precedence() { 519 // parens will be inserted. 520 // pretend this is an *ast.ParenExpr and do nothing. 521 break 522 } 523 h4, h5, mp := walkBinary(r) 524 has4 = has4 || h4 525 has5 = has5 || h5 526 if maxProblem < mp { 527 maxProblem = mp 528 } 529 530 case *ast.StarExpr: 531 if e.Op == token.QUO { // `*/` 532 maxProblem = 5 533 } 534 535 case *ast.UnaryExpr: 536 switch e.Op.String() + r.Op.String() { 537 case "/*", "&&", "&^": 538 maxProblem = 5 539 case "++", "--": 540 if maxProblem < 4 { 541 maxProblem = 4 542 } 543 } 544 } 545 return 546 } 547 548 func cutoff(e *ast.BinaryExpr, depth int) int { 549 has4, has5, maxProblem := walkBinary(e) 550 if maxProblem > 0 { 551 return maxProblem + 1 552 } 553 if has4 && has5 { 554 if depth == 1 { 555 return 5 556 } 557 return 4 558 } 559 if depth == 1 { 560 return 6 561 } 562 return 4 563 } 564 565 func diffPrec(expr ast.Expr, prec int) int { 566 x, ok := expr.(*ast.BinaryExpr) 567 if !ok || prec != x.Op.Precedence() { 568 return 1 569 } 570 return 0 571 } 572 573 func reduceDepth(depth int) int { 574 depth-- 575 if depth < 1 { 576 depth = 1 577 } 578 return depth 579 } 580 581 // Format the binary expression: decide the cutoff and then format. 582 // Let's call depth == 1 Normal mode, and depth > 1 Compact mode. 583 // (Algorithm suggestion by Russ Cox.) 584 // 585 // The precedences are: 586 // 5 * / % << >> & &^ 587 // 4 + - | ^ 588 // 3 == != < <= > >= 589 // 2 && 590 // 1 || 591 // 592 // The only decision is whether there will be spaces around levels 4 and 5. 593 // There are never spaces at level 6 (unary), and always spaces at levels 3 and below. 594 // 595 // To choose the cutoff, look at the whole expression but excluding primary 596 // expressions (function calls, parenthesized exprs), and apply these rules: 597 // 598 // 1) If there is a binary operator with a right side unary operand 599 // that would clash without a space, the cutoff must be (in order): 600 // 601 // /* 6 602 // && 6 603 // &^ 6 604 // ++ 5 605 // -- 5 606 // 607 // (Comparison operators always have spaces around them.) 608 // 609 // 2) If there is a mix of level 5 and level 4 operators, then the cutoff 610 // is 5 (use spaces to distinguish precedence) in Normal mode 611 // and 4 (never use spaces) in Compact mode. 612 // 613 // 3) If there are no level 4 operators or no level 5 operators, then the 614 // cutoff is 6 (always use spaces) in Normal mode 615 // and 4 (never use spaces) in Compact mode. 616 // 617 func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) { 618 prec := x.Op.Precedence() 619 if prec < prec1 { 620 // parenthesis needed 621 // Note: The parser inserts an ast.ParenExpr node; thus this case 622 // can only occur if the AST is created in a different way. 623 p.print(token.LPAREN) 624 p.expr0(x, reduceDepth(depth)) // parentheses undo one level of depth 625 p.print(token.RPAREN) 626 return 627 } 628 629 printBlank := prec < cutoff 630 631 ws := indent 632 p.expr1(x.X, prec, depth+diffPrec(x.X, prec)) 633 if printBlank { 634 p.print(blank) 635 } 636 xline := p.pos.Line // before the operator (it may be on the next line!) 637 yline := p.lineFor(x.Y.Pos()) 638 p.print(x.OpPos, x.Op) 639 if xline != yline && xline > 0 && yline > 0 { 640 // at least one line break, but respect an extra empty line 641 // in the source 642 if p.linebreak(yline, 1, ws, true) { 643 ws = ignore 644 printBlank = false // no blank after line break 645 } 646 } 647 if printBlank { 648 p.print(blank) 649 } 650 p.expr1(x.Y, prec+1, depth+1) 651 if ws == ignore { 652 p.print(unindent) 653 } 654 } 655 656 func isBinary(expr ast.Expr) bool { 657 _, ok := expr.(*ast.BinaryExpr) 658 return ok 659 } 660 661 func (p *printer) expr1(expr ast.Expr, prec1, depth int) { 662 p.print(expr.Pos()) 663 664 switch x := expr.(type) { 665 case *ast.BadExpr: 666 p.print("BadExpr") 667 668 case *ast.Ident: 669 p.print(x) 670 671 case *ast.BinaryExpr: 672 if depth < 1 { 673 p.internalError("depth < 1:", depth) 674 depth = 1 675 } 676 p.binaryExpr(x, prec1, cutoff(x, depth), depth) 677 678 case *ast.KeyValueExpr: 679 p.expr(x.Key) 680 p.print(x.Colon, token.COLON, blank) 681 p.expr(x.Value) 682 683 case *ast.StarExpr: 684 const prec = token.UnaryPrec 685 if prec < prec1 { 686 // parenthesis needed 687 p.print(token.LPAREN) 688 p.print(token.MUL) 689 p.expr(x.X) 690 p.print(token.RPAREN) 691 } else { 692 // no parenthesis needed 693 p.print(token.MUL) 694 p.expr(x.X) 695 } 696 697 case *ast.UnaryExpr: 698 const prec = token.UnaryPrec 699 if prec < prec1 { 700 // parenthesis needed 701 p.print(token.LPAREN) 702 p.expr(x) 703 p.print(token.RPAREN) 704 } else { 705 // no parenthesis needed 706 p.print(x.Op) 707 if x.Op == token.RANGE { 708 // TODO(gri) Remove this code if it cannot be reached. 709 p.print(blank) 710 } 711 p.expr1(x.X, prec, depth) 712 } 713 714 case *ast.BasicLit: 715 p.print(x) 716 717 case *ast.FuncLit: 718 p.expr(x.Type) 719 p.funcBody(x.Body, p.distance(x.Type.Pos(), p.pos), true) 720 721 case *ast.ParenExpr: 722 if _, hasParens := x.X.(*ast.ParenExpr); hasParens { 723 // don't print parentheses around an already parenthesized expression 724 // TODO(gri) consider making this more general and incorporate precedence levels 725 p.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth 726 } else { 727 p.print(token.LPAREN) 728 p.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth 729 p.print(x.Rparen, token.RPAREN) 730 } 731 732 case *ast.SelectorExpr: 733 p.expr1(x.X, token.HighestPrec, depth) 734 p.print(token.PERIOD) 735 if line := p.lineFor(x.Sel.Pos()); p.pos.IsValid() && p.pos.Line < line { 736 p.print(indent, newline, x.Sel.Pos(), x.Sel, unindent) 737 } else { 738 p.print(x.Sel.Pos(), x.Sel) 739 } 740 741 case *ast.TypeAssertExpr: 742 p.expr1(x.X, token.HighestPrec, depth) 743 p.print(token.PERIOD, token.LPAREN) 744 if x.Type != nil { 745 p.expr(x.Type) 746 } else { 747 p.print(token.TYPE) 748 } 749 p.print(token.RPAREN) 750 751 case *ast.IndexExpr: 752 // TODO(gri): should treat[] like parentheses and undo one level of depth 753 p.expr1(x.X, token.HighestPrec, 1) 754 p.print(x.Lbrack, token.LBRACK) 755 p.expr0(x.Index, depth+1) 756 p.print(x.Rbrack, token.RBRACK) 757 758 case *ast.SliceExpr: 759 // TODO(gri): should treat[] like parentheses and undo one level of depth 760 p.expr1(x.X, token.HighestPrec, 1) 761 p.print(x.Lbrack, token.LBRACK) 762 if x.Low != nil { 763 p.expr0(x.Low, depth+1) 764 } 765 // blanks around ":" if both sides exist and either side is a binary expression 766 if depth <= 1 && x.Low != nil && x.High != nil && (isBinary(x.Low) || isBinary(x.High)) { 767 p.print(blank, token.COLON, blank) 768 } else { 769 p.print(token.COLON) 770 } 771 if x.High != nil { 772 p.expr0(x.High, depth+1) 773 } 774 p.print(x.Rbrack, token.RBRACK) 775 776 case *ast.CallExpr: 777 if len(x.Args) > 1 { 778 depth++ 779 } 780 p.expr1(x.Fun, token.HighestPrec, depth) 781 p.print(x.Lparen, token.LPAREN) 782 if x.Ellipsis.IsValid() { 783 p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis) 784 p.print(x.Ellipsis, token.ELLIPSIS) 785 if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) { 786 p.print(token.COMMA, formfeed) 787 } 788 } else { 789 p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen) 790 } 791 p.print(x.Rparen, token.RPAREN) 792 793 case *ast.CompositeLit: 794 // composite literal elements that are composite literals themselves may have the type omitted 795 if x.Type != nil { 796 p.expr1(x.Type, token.HighestPrec, depth) 797 } 798 p.print(x.Lbrace, token.LBRACE) 799 p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace) 800 // do not insert extra line breaks because of comments before 801 // the closing '}' as it might break the code if there is no 802 // trailing ',' 803 p.print(noExtraLinebreak, x.Rbrace, token.RBRACE, noExtraLinebreak) 804 805 case *ast.Ellipsis: 806 p.print(token.ELLIPSIS) 807 if x.Elt != nil { 808 p.expr(x.Elt) 809 } 810 811 case *ast.ArrayType: 812 p.print(token.LBRACK) 813 if x.Len != nil { 814 p.expr(x.Len) 815 } 816 p.print(token.RBRACK) 817 p.expr(x.Elt) 818 819 case *ast.StructType: 820 p.print(token.STRUCT) 821 p.fieldList(x.Fields, true, x.Incomplete) 822 823 case *ast.FuncType: 824 p.print(token.FUNC) 825 p.signature(x.Params, x.Results) 826 827 case *ast.InterfaceType: 828 p.print(token.INTERFACE) 829 p.fieldList(x.Methods, false, x.Incomplete) 830 831 case *ast.MapType: 832 p.print(token.MAP, token.LBRACK) 833 p.expr(x.Key) 834 p.print(token.RBRACK) 835 p.expr(x.Value) 836 837 case *ast.ChanType: 838 switch x.Dir { 839 case ast.SEND | ast.RECV: 840 p.print(token.CHAN) 841 case ast.RECV: 842 p.print(token.ARROW, token.CHAN) 843 case ast.SEND: 844 p.print(token.CHAN, token.ARROW) 845 } 846 p.print(blank) 847 p.expr(x.Value) 848 849 default: 850 panic("unreachable") 851 } 852 853 return 854 } 855 856 func (p *printer) expr0(x ast.Expr, depth int) { 857 p.expr1(x, token.LowestPrec, depth) 858 } 859 860 func (p *printer) expr(x ast.Expr) { 861 const depth = 1 862 p.expr1(x, token.LowestPrec, depth) 863 } 864 865 // ---------------------------------------------------------------------------- 866 // Statements 867 868 // Print the statement list indented, but without a newline after the last statement. 869 // Extra line breaks between statements in the source are respected but at most one 870 // empty line is printed between statements. 871 func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) { 872 // TODO(gri): fix _indent code 873 if _indent > 0 { 874 p.print(indent) 875 } 876 multiLine := false 877 for i, s := range list { 878 // _indent == 0 only for lists of switch/select case clauses; 879 // in those cases each clause is a new section 880 p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || _indent == 0 || multiLine) 881 p.stmt(s, nextIsRBrace && i == len(list)-1) 882 multiLine = p.isMultiLine(s) 883 } 884 if _indent > 0 { 885 p.print(unindent) 886 } 887 } 888 889 // block prints an *ast.BlockStmt; it always spans at least two lines. 890 func (p *printer) block(s *ast.BlockStmt, indent int) { 891 p.print(s.Pos(), token.LBRACE) 892 p.stmtList(s.List, indent, true) 893 p.linebreak(p.lineFor(s.Rbrace), 1, ignore, true) 894 p.print(s.Rbrace, token.RBRACE) 895 } 896 897 func isTypeName(x ast.Expr) bool { 898 switch t := x.(type) { 899 case *ast.Ident: 900 return true 901 case *ast.SelectorExpr: 902 return isTypeName(t.X) 903 } 904 return false 905 } 906 907 func stripParens(x ast.Expr) ast.Expr { 908 if px, strip := x.(*ast.ParenExpr); strip { 909 // parentheses must not be stripped if there are any 910 // unparenthesized composite literals starting with 911 // a type name 912 ast.Inspect(px.X, func(node ast.Node) bool { 913 switch x := node.(type) { 914 case *ast.ParenExpr: 915 // parentheses protect enclosed composite literals 916 return false 917 case *ast.CompositeLit: 918 if isTypeName(x.Type) { 919 strip = false // do not strip parentheses 920 } 921 return false 922 } 923 // in all other cases, keep inspecting 924 return true 925 }) 926 if strip { 927 return stripParens(px.X) 928 } 929 } 930 return x 931 } 932 933 func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) { 934 p.print(blank) 935 needsBlank := false 936 if init == nil && post == nil { 937 // no semicolons required 938 if expr != nil { 939 p.expr(stripParens(expr)) 940 needsBlank = true 941 } 942 } else { 943 // all semicolons required 944 // (they are not separators, print them explicitly) 945 if init != nil { 946 p.stmt(init, false) 947 } 948 p.print(token.SEMICOLON, blank) 949 if expr != nil { 950 p.expr(stripParens(expr)) 951 needsBlank = true 952 } 953 if isForStmt { 954 p.print(token.SEMICOLON, blank) 955 needsBlank = false 956 if post != nil { 957 p.stmt(post, false) 958 needsBlank = true 959 } 960 } 961 } 962 if needsBlank { 963 p.print(blank) 964 } 965 } 966 967 // indentList reports whether an expression list would look better if it 968 // were indented wholesale (starting with the very first element, rather 969 // than starting at the first line break). 970 // 971 func (p *printer) indentList(list []ast.Expr) bool { 972 // Heuristic: indentList returns true if there are more than one multi- 973 // line element in the list, or if there is any element that is not 974 // starting on the same line as the previous one ends. 975 if len(list) >= 2 { 976 var b = p.lineFor(list[0].Pos()) 977 var e = p.lineFor(list[len(list)-1].End()) 978 if 0 < b && b < e { 979 // list spans multiple lines 980 n := 0 // multi-line element count 981 line := b 982 for _, x := range list { 983 xb := p.lineFor(x.Pos()) 984 xe := p.lineFor(x.End()) 985 if line < xb { 986 // x is not starting on the same 987 // line as the previous one ended 988 return true 989 } 990 if xb < xe { 991 // x is a multi-line element 992 n++ 993 } 994 line = xe 995 } 996 return n > 1 997 } 998 } 999 return false 1000 } 1001 1002 func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) { 1003 p.print(stmt.Pos()) 1004 1005 switch s := stmt.(type) { 1006 case *ast.BadStmt: 1007 p.print("BadStmt") 1008 1009 case *ast.DeclStmt: 1010 p.decl(s.Decl) 1011 1012 case *ast.EmptyStmt: 1013 // nothing to do 1014 1015 case *ast.LabeledStmt: 1016 // a "correcting" unindent immediately following a line break 1017 // is applied before the line break if there is no comment 1018 // between (see writeWhitespace) 1019 p.print(unindent) 1020 p.expr(s.Label) 1021 p.print(s.Colon, token.COLON, indent) 1022 if e, isEmpty := s.Stmt.(*ast.EmptyStmt); isEmpty { 1023 if !nextIsRBrace { 1024 p.print(newline, e.Pos(), token.SEMICOLON) 1025 break 1026 } 1027 } else { 1028 p.linebreak(p.lineFor(s.Stmt.Pos()), 1, ignore, true) 1029 } 1030 p.stmt(s.Stmt, nextIsRBrace) 1031 1032 case *ast.ExprStmt: 1033 const depth = 1 1034 p.expr0(s.X, depth) 1035 1036 case *ast.SendStmt: 1037 const depth = 1 1038 p.expr0(s.Chan, depth) 1039 p.print(blank, s.Arrow, token.ARROW, blank) 1040 p.expr0(s.Value, depth) 1041 1042 case *ast.IncDecStmt: 1043 const depth = 1 1044 p.expr0(s.X, depth+1) 1045 p.print(s.TokPos, s.Tok) 1046 1047 case *ast.AssignStmt: 1048 var depth = 1 1049 if len(s.Lhs) > 1 && len(s.Rhs) > 1 { 1050 depth++ 1051 } 1052 p.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos) 1053 p.print(blank, s.TokPos, s.Tok, blank) 1054 p.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos) 1055 1056 case *ast.GoStmt: 1057 p.print(token.GO, blank) 1058 p.expr(s.Call) 1059 1060 case *ast.DeferStmt: 1061 p.print(token.DEFER, blank) 1062 p.expr(s.Call) 1063 1064 case *ast.ReturnStmt: 1065 p.print(token.RETURN) 1066 if s.Results != nil { 1067 p.print(blank) 1068 // Use indentList heuristic to make corner cases look 1069 // better (issue 1207). A more systematic approach would 1070 // always indent, but this would cause significant 1071 // reformatting of the code base and not necessarily 1072 // lead to more nicely formatted code in general. 1073 if p.indentList(s.Results) { 1074 p.print(indent) 1075 p.exprList(s.Pos(), s.Results, 1, noIndent, token.NoPos) 1076 p.print(unindent) 1077 } else { 1078 p.exprList(s.Pos(), s.Results, 1, 0, token.NoPos) 1079 } 1080 } 1081 1082 case *ast.BranchStmt: 1083 p.print(s.Tok) 1084 if s.Label != nil { 1085 p.print(blank) 1086 p.expr(s.Label) 1087 } 1088 1089 case *ast.BlockStmt: 1090 p.block(s, 1) 1091 1092 case *ast.IfStmt: 1093 p.print(token.IF) 1094 p.controlClause(false, s.Init, s.Cond, nil) 1095 p.block(s.Body, 1) 1096 if s.Else != nil { 1097 p.print(blank, token.ELSE, blank) 1098 switch s.Else.(type) { 1099 case *ast.BlockStmt, *ast.IfStmt: 1100 p.stmt(s.Else, nextIsRBrace) 1101 default: 1102 p.print(token.LBRACE, indent, formfeed) 1103 p.stmt(s.Else, true) 1104 p.print(unindent, formfeed, token.RBRACE) 1105 } 1106 } 1107 1108 case *ast.CaseClause: 1109 if s.List != nil { 1110 p.print(token.CASE, blank) 1111 p.exprList(s.Pos(), s.List, 1, 0, s.Colon) 1112 } else { 1113 p.print(token.DEFAULT) 1114 } 1115 p.print(s.Colon, token.COLON) 1116 p.stmtList(s.Body, 1, nextIsRBrace) 1117 1118 case *ast.SwitchStmt: 1119 p.print(token.SWITCH) 1120 p.controlClause(false, s.Init, s.Tag, nil) 1121 p.block(s.Body, 0) 1122 1123 case *ast.TypeSwitchStmt: 1124 p.print(token.SWITCH) 1125 if s.Init != nil { 1126 p.print(blank) 1127 p.stmt(s.Init, false) 1128 p.print(token.SEMICOLON) 1129 } 1130 p.print(blank) 1131 p.stmt(s.Assign, false) 1132 p.print(blank) 1133 p.block(s.Body, 0) 1134 1135 case *ast.CommClause: 1136 if s.Comm != nil { 1137 p.print(token.CASE, blank) 1138 p.stmt(s.Comm, false) 1139 } else { 1140 p.print(token.DEFAULT) 1141 } 1142 p.print(s.Colon, token.COLON) 1143 p.stmtList(s.Body, 1, nextIsRBrace) 1144 1145 case *ast.SelectStmt: 1146 p.print(token.SELECT, blank) 1147 body := s.Body 1148 if len(body.List) == 0 && !p.commentBefore(p.posFor(body.Rbrace)) { 1149 // print empty select statement w/o comments on one line 1150 p.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE) 1151 } else { 1152 p.block(body, 0) 1153 } 1154 1155 case *ast.ForStmt: 1156 p.print(token.FOR) 1157 p.controlClause(true, s.Init, s.Cond, s.Post) 1158 p.block(s.Body, 1) 1159 1160 case *ast.RangeStmt: 1161 p.print(token.FOR, blank) 1162 p.expr(s.Key) 1163 if s.Value != nil { 1164 // use position of value following the comma as 1165 // comma position for correct comment placement 1166 p.print(s.Value.Pos(), token.COMMA, blank) 1167 p.expr(s.Value) 1168 } 1169 p.print(blank, s.TokPos, s.Tok, blank, token.RANGE, blank) 1170 p.expr(stripParens(s.X)) 1171 p.print(blank) 1172 p.block(s.Body, 1) 1173 1174 default: 1175 panic("unreachable") 1176 } 1177 1178 return 1179 } 1180 1181 // ---------------------------------------------------------------------------- 1182 // Declarations 1183 1184 // The keepTypeColumn function determines if the type column of a series of 1185 // consecutive const or var declarations must be kept, or if initialization 1186 // values (V) can be placed in the type column (T) instead. The i'th entry 1187 // in the result slice is true if the type column in spec[i] must be kept. 1188 // 1189 // For example, the declaration: 1190 // 1191 // const ( 1192 // foobar int = 42 // comment 1193 // x = 7 // comment 1194 // foo 1195 // bar = 991 1196 // ) 1197 // 1198 // leads to the type/values matrix below. A run of value columns (V) can 1199 // be moved into the type column if there is no type for any of the values 1200 // in that column (we only move entire columns so that they align properly). 1201 // 1202 // matrix formatted result 1203 // matrix 1204 // T V -> T V -> true there is a T and so the type 1205 // - V - V true column must be kept 1206 // - - - - false 1207 // - V V - false V is moved into T column 1208 // 1209 func keepTypeColumn(specs []ast.Spec) []bool { 1210 m := make([]bool, len(specs)) 1211 1212 populate := func(i, j int, keepType bool) { 1213 if keepType { 1214 for ; i < j; i++ { 1215 m[i] = true 1216 } 1217 } 1218 } 1219 1220 i0 := -1 // if i0 >= 0 we are in a run and i0 is the start of the run 1221 var keepType bool 1222 for i, s := range specs { 1223 t := s.(*ast.ValueSpec) 1224 if t.Values != nil { 1225 if i0 < 0 { 1226 // start of a run of ValueSpecs with non-nil Values 1227 i0 = i 1228 keepType = false 1229 } 1230 } else { 1231 if i0 >= 0 { 1232 // end of a run 1233 populate(i0, i, keepType) 1234 i0 = -1 1235 } 1236 } 1237 if t.Type != nil { 1238 keepType = true 1239 } 1240 } 1241 if i0 >= 0 { 1242 // end of a run 1243 populate(i0, len(specs), keepType) 1244 } 1245 1246 return m 1247 } 1248 1249 func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) { 1250 p.setComment(s.Doc) 1251 p.identList(s.Names, false) // always present 1252 extraTabs := 3 1253 if s.Type != nil || keepType { 1254 p.print(vtab) 1255 extraTabs-- 1256 } 1257 if s.Type != nil { 1258 p.expr(s.Type) 1259 } 1260 if s.Values != nil { 1261 p.print(vtab, token.ASSIGN, blank) 1262 p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos) 1263 extraTabs-- 1264 } 1265 if s.Comment != nil { 1266 for ; extraTabs > 0; extraTabs-- { 1267 p.print(vtab) 1268 } 1269 p.setComment(s.Comment) 1270 } 1271 } 1272 1273 // The parameter n is the number of specs in the group. If doIndent is set, 1274 // multi-line identifier lists in the spec are indented when the first 1275 // linebreak is encountered. 1276 // 1277 func (p *printer) spec(spec ast.Spec, n int, doIndent bool) { 1278 switch s := spec.(type) { 1279 case *ast.ImportSpec: 1280 p.setComment(s.Doc) 1281 if s.Name != nil { 1282 p.expr(s.Name) 1283 p.print(blank) 1284 } 1285 p.expr(s.Path) 1286 p.setComment(s.Comment) 1287 p.print(s.EndPos) 1288 1289 case *ast.ValueSpec: 1290 if n != 1 { 1291 p.internalError("expected n = 1; got", n) 1292 } 1293 p.setComment(s.Doc) 1294 p.identList(s.Names, doIndent) // always present 1295 if s.Type != nil { 1296 p.print(blank) 1297 p.expr(s.Type) 1298 } 1299 if s.Values != nil { 1300 p.print(blank, token.ASSIGN, blank) 1301 p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos) 1302 } 1303 p.setComment(s.Comment) 1304 1305 case *ast.TypeSpec: 1306 p.setComment(s.Doc) 1307 p.expr(s.Name) 1308 if n == 1 { 1309 p.print(blank) 1310 } else { 1311 p.print(vtab) 1312 } 1313 p.expr(s.Type) 1314 p.setComment(s.Comment) 1315 1316 default: 1317 panic("unreachable") 1318 } 1319 } 1320 1321 func (p *printer) genDecl(d *ast.GenDecl) { 1322 p.setComment(d.Doc) 1323 p.print(d.Pos(), d.Tok, blank) 1324 1325 if d.Lparen.IsValid() { 1326 // group of parenthesized declarations 1327 p.print(d.Lparen, token.LPAREN) 1328 if n := len(d.Specs); n > 0 { 1329 p.print(indent, formfeed) 1330 if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) { 1331 // two or more grouped const/var declarations: 1332 // determine if the type column must be kept 1333 keepType := keepTypeColumn(d.Specs) 1334 newSection := false 1335 for i, s := range d.Specs { 1336 if i > 0 { 1337 p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection) 1338 } 1339 p.valueSpec(s.(*ast.ValueSpec), keepType[i]) 1340 newSection = p.isMultiLine(s) 1341 } 1342 } else { 1343 newSection := false 1344 for i, s := range d.Specs { 1345 if i > 0 { 1346 p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection) 1347 } 1348 p.spec(s, n, false) 1349 newSection = p.isMultiLine(s) 1350 } 1351 } 1352 p.print(unindent, formfeed) 1353 } 1354 p.print(d.Rparen, token.RPAREN) 1355 1356 } else { 1357 // single declaration 1358 p.spec(d.Specs[0], 1, true) 1359 } 1360 } 1361 1362 // nodeSize determines the size of n in chars after formatting. 1363 // The result is <= maxSize if the node fits on one line with at 1364 // most maxSize chars and the formatted output doesn't contain 1365 // any control chars. Otherwise, the result is > maxSize. 1366 // 1367 func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) { 1368 // nodeSize invokes the printer, which may invoke nodeSize 1369 // recursively. For deep composite literal nests, this can 1370 // lead to an exponential algorithm. Remember previous 1371 // results to prune the recursion (was issue 1628). 1372 if size, found := p.nodeSizes[n]; found { 1373 return size 1374 } 1375 1376 size = maxSize + 1 // assume n doesn't fit 1377 p.nodeSizes[n] = size 1378 1379 // nodeSize computation must be independent of particular 1380 // style so that we always get the same decision; print 1381 // in RawFormat 1382 cfg := Config{Mode: RawFormat} 1383 var buf bytes.Buffer 1384 if err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil { 1385 return 1386 } 1387 if buf.Len() <= maxSize { 1388 for _, ch := range buf.Bytes() { 1389 if ch < ' ' { 1390 return 1391 } 1392 } 1393 size = buf.Len() // n fits 1394 p.nodeSizes[n] = size 1395 } 1396 return 1397 } 1398 1399 func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool { 1400 pos1 := b.Pos() 1401 pos2 := b.Rbrace 1402 if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) { 1403 // opening and closing brace are on different lines - don't make it a one-liner 1404 return false 1405 } 1406 if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) { 1407 // too many statements or there is a comment inside - don't make it a one-liner 1408 return false 1409 } 1410 // otherwise, estimate body size 1411 const maxSize = 100 1412 bodySize := 0 1413 for i, s := range b.List { 1414 if i > 0 { 1415 bodySize += 2 // space for a semicolon and blank 1416 } 1417 bodySize += p.nodeSize(s, maxSize) 1418 } 1419 return headerSize+bodySize <= maxSize 1420 } 1421 1422 func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) { 1423 if b == nil { 1424 return 1425 } 1426 1427 if p.isOneLineFunc(b, headerSize) { 1428 sep := vtab 1429 if isLit { 1430 sep = blank 1431 } 1432 p.print(sep, b.Lbrace, token.LBRACE) 1433 if len(b.List) > 0 { 1434 p.print(blank) 1435 for i, s := range b.List { 1436 if i > 0 { 1437 p.print(token.SEMICOLON, blank) 1438 } 1439 p.stmt(s, i == len(b.List)-1) 1440 } 1441 p.print(blank) 1442 } 1443 p.print(b.Rbrace, token.RBRACE) 1444 return 1445 } 1446 1447 p.print(blank) 1448 p.block(b, 1) 1449 } 1450 1451 // distance returns the column difference between from and to if both 1452 // are on the same line; if they are on different lines (or unknown) 1453 // the result is infinity. 1454 func (p *printer) distance(from0 token.Pos, to token.Position) int { 1455 from := p.posFor(from0) 1456 if from.IsValid() && to.IsValid() && from.Line == to.Line { 1457 return to.Column - from.Column 1458 } 1459 return infinity 1460 } 1461 1462 func (p *printer) funcDecl(d *ast.FuncDecl) { 1463 p.setComment(d.Doc) 1464 p.print(d.Pos(), token.FUNC, blank) 1465 if d.Recv != nil { 1466 p.parameters(d.Recv) // method: print receiver 1467 p.print(blank) 1468 } 1469 p.expr(d.Name) 1470 p.signature(d.Type.Params, d.Type.Results) 1471 p.funcBody(d.Body, p.distance(d.Pos(), p.pos), false) 1472 } 1473 1474 func (p *printer) decl(decl ast.Decl) { 1475 switch d := decl.(type) { 1476 case *ast.BadDecl: 1477 p.print(d.Pos(), "BadDecl") 1478 case *ast.GenDecl: 1479 p.genDecl(d) 1480 case *ast.FuncDecl: 1481 p.funcDecl(d) 1482 default: 1483 panic("unreachable") 1484 } 1485 } 1486 1487 // ---------------------------------------------------------------------------- 1488 // Files 1489 1490 func declToken(decl ast.Decl) (tok token.Token) { 1491 tok = token.ILLEGAL 1492 switch d := decl.(type) { 1493 case *ast.GenDecl: 1494 tok = d.Tok 1495 case *ast.FuncDecl: 1496 tok = token.FUNC 1497 } 1498 return 1499 } 1500 1501 func (p *printer) file(src *ast.File) { 1502 p.setComment(src.Doc) 1503 p.print(src.Pos(), token.PACKAGE, blank) 1504 p.expr(src.Name) 1505 1506 if len(src.Decls) > 0 { 1507 tok := token.ILLEGAL 1508 for _, d := range src.Decls { 1509 prev := tok 1510 tok = declToken(d) 1511 // if the declaration token changed (e.g., from CONST to TYPE) 1512 // or the next declaration has documentation associated with it, 1513 // print an empty line between top-level declarations 1514 // (because p.linebreak is called with the position of d, which 1515 // is past any documentation, the minimum requirement is satisfied 1516 // even w/o the extra getDoc(d) nil-check - leave it in case the 1517 // linebreak logic improves - there's already a TODO). 1518 min := 1 1519 if prev != tok || getDoc(d) != nil { 1520 min = 2 1521 } 1522 p.linebreak(p.lineFor(d.Pos()), min, ignore, false) 1523 p.decl(d) 1524 } 1525 } 1526 1527 p.print(newline) 1528 }