go/printer, gofmt: improved comment placement

Applied gofmt -w src misc (no changes).

Fixes #3147.

R=r, rsc
CC=golang-dev
https://golang.org/cl/5710046
diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go
index 1ab4456..a027d32 100644
--- a/src/pkg/go/printer/printer.go
+++ b/src/pkg/go/printer/printer.go
@@ -277,10 +277,9 @@
 // it as is likely to help position the comment nicely.
 // pos is the comment position, next the position of the item
 // after all pending comments, prev is the previous comment in
-// a group of comments (or nil), and isKeyword indicates if the
-// next item is a keyword.
+// a group of comments (or nil), and tok is the next token.
 //
-func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *ast.Comment, isKeyword bool) {
+func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *ast.Comment, tok token.Token) {
 	if len(p.output) == 0 {
 		// the comment is the first item to be printed - don't write any whitespace
 		return
@@ -335,38 +334,41 @@
 		// comment on a different line:
 		// separate with at least one line break
 		droppedLinebreak := false
-		if prev == nil {
-			// first comment of a comment group
-			j := 0
-			for i, ch := range p.wsbuf {
-				switch ch {
-				case blank, vtab:
-					// ignore any horizontal whitespace before line breaks
-					p.wsbuf[i] = ignore
+		j := 0
+		for i, ch := range p.wsbuf {
+			switch ch {
+			case blank, vtab:
+				// ignore any horizontal whitespace before line breaks
+				p.wsbuf[i] = ignore
+				continue
+			case indent:
+				// apply pending indentation
+				continue
+			case unindent:
+				// if this is not the last unindent, apply it
+				// as it is (likely) belonging to the last
+				// construct (e.g., a multi-line expression list)
+				// and is not part of closing a block
+				if i+1 < len(p.wsbuf) && p.wsbuf[i+1] == unindent {
 					continue
-				case indent:
-					// apply pending indentation
-					continue
-				case unindent:
-					// if the next token is a keyword, apply the outdent
-					// if it appears that the comment is aligned with the
-					// keyword; otherwise assume the outdent is part of a
-					// closing block and stop (this scenario appears with
-					// comments before a case label where the comments
-					// apply to the next case instead of the current one)
-					if isKeyword && pos.Column == next.Column {
-						continue
-					}
-				case newline, formfeed:
-					// TODO(gri): may want to keep formfeed info in some cases
-					p.wsbuf[i] = ignore
-					droppedLinebreak = true
 				}
-				j = i
-				break
+				// if the next token is not a closing }, apply the unindent
+				// if it appears that the comment is aligned with the
+				// token; otherwise assume the unindent is part of a
+				// closing block and stop (this scenario appears with
+				// comments before a case label where the comments
+				// apply to the next case instead of the current one)
+				if tok != token.RBRACE && pos.Column == next.Column {
+					continue
+				}
+			case newline, formfeed:
+				p.wsbuf[i] = ignore
+				droppedLinebreak = prev == nil // record only if first comment of a group
 			}
-			p.writeWhitespace(j)
+			j = i
+			break
 		}
+		p.writeWhitespace(j)
 
 		// determine number of linebreaks before the comment
 		n := 0
@@ -675,7 +677,7 @@
 	var last *ast.Comment
 	for p.commentBefore(next) {
 		for _, c := range p.comment.List {
-			p.writeCommentPrefix(p.posFor(c.Pos()), next, last, c, tok.IsKeyword())
+			p.writeCommentPrefix(p.posFor(c.Pos()), next, last, c, tok)
 			p.writeComment(c)
 			last = c
 		}
diff --git a/src/pkg/go/printer/testdata/comments.golden b/src/pkg/go/printer/testdata/comments.golden
index 4c6f1ab..d9aa2d8 100644
--- a/src/pkg/go/printer/testdata/comments.golden
+++ b/src/pkg/go/printer/testdata/comments.golden
@@ -168,6 +168,91 @@
 	// this comment should not be indented
 }
 
+//
+// Indentation of comments after possibly indented multi-line constructs
+// (test cases for issue 3147).
+//
+
+func _() {
+	s := 1 +
+		2
+	// should be indented like s
+}
+
+func _() {
+	s := 1 +
+		2	// comment
+	// should be indented like s
+}
+
+func _() {
+	s := 1 +
+		2	// comment
+	// should be indented like s
+	_ = 0
+}
+
+func _() {
+	s := 1 +
+		2
+	// should be indented like s
+	_ = 0
+}
+
+func _() {
+	s := 1 +
+		2
+
+	// should be indented like s
+}
+
+func _() {
+	s := 1 +
+		2	// comment
+
+	// should be indented like s
+}
+
+func _() {
+	s := 1 +
+		2	// comment
+
+	// should be indented like s
+	_ = 0
+}
+
+func _() {
+	s := 1 +
+		2
+
+	// should be indented like s
+	_ = 0
+}
+
+// Test case from issue 3147.
+func f() {
+	templateText := "a" +	// A
+		"b" +	// B
+		"c"	// C
+
+	// should be aligned with f()
+	f()
+}
+
+// Modified test case from issue 3147.
+func f() {
+	templateText := "a" +	// A
+		"b" +	// B
+		"c"	// C
+
+		// may not be aligned with f() (source is not aligned)
+	f()
+}
+
+//
+// Test cases for alignment of lines in general comments.
+//
+
 func _() {
 	/* freestanding comment
 	   aligned		line
diff --git a/src/pkg/go/printer/testdata/comments.input b/src/pkg/go/printer/testdata/comments.input
index c0f8cca..6084b3f 100644
--- a/src/pkg/go/printer/testdata/comments.input
+++ b/src/pkg/go/printer/testdata/comments.input
@@ -171,6 +171,91 @@
 	// this comment should not be indented
 }
 
+//
+// Indentation of comments after possibly indented multi-line constructs
+// (test cases for issue 3147).
+//
+
+func _() {
+	s := 1 +
+		2
+// should be indented like s
+}
+
+func _() {
+	s := 1 +
+		2 // comment
+		// should be indented like s
+}
+
+func _() {
+	s := 1 +
+		2 // comment
+	// should be indented like s
+	_ = 0
+}
+
+func _() {
+	s := 1 +
+		2
+	// should be indented like s
+	_ = 0
+}
+
+func _() {
+	s := 1 +
+		2
+
+// should be indented like s
+}
+
+func _() {
+	s := 1 +
+		2 // comment
+
+		// should be indented like s
+}
+
+func _() {
+	s := 1 +
+		2 // comment
+
+	// should be indented like s
+	_ = 0
+}
+
+func _() {
+	s := 1 +
+		2
+
+	// should be indented like s
+	_ = 0
+}
+
+// Test case from issue 3147.
+func f() {
+	templateText := "a" + // A
+		"b" + // B
+		"c" // C
+
+	// should be aligned with f()
+	f()
+}
+
+// Modified test case from issue 3147.
+func f() {
+	templateText := "a" + // A
+		"b" + // B
+		"c" // C
+
+		// may not be aligned with f() (source is not aligned)
+	f()
+}
+
+//
+// Test cases for alignment of lines in general comments.
+//
+
 func _() {
 	/* freestanding comment
 	   aligned		line