From: Robert Griesemer Date: Thu, 9 Jul 2009 23:38:17 +0000 (-0700) Subject: Show BUG comments in godoc: X-Git-Tag: weekly.2009-11-06~1197 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=38e7fddc21e2c1c5fbf4f332401b999800a8b5e5;p=gostls13.git Show BUG comments in godoc: ast.go: - rename Comments -> CommentGroup (less confusion) - change all comments/docs to be *CommentGroup filter.go: - do not remove unassociated comments from program as part of export filtering (they are needed by doc.go for BUG comments) scanner.go: - exclude '\n' from //-style comments parser.go: - rewrote collection of comments: now all collected comments are *ast.CommentGroups - clarified distinction between leading and trailing comments - fixed a bug in comment collection (parseGenDecl); corresponding test case is in printer/testdata - extra documentation doc.go: - collect BUG comments - corresponding fix for parser bug in comment collection comment.go: - simplified regex printer.go: - adjust comment printing to new representation printer_test.go, testdata/*: - enable printing of doc comments - extended tests package.html, package.txt: - added Bugs section gofmt: - enable printing of doc comments R=rsc DELTA=339 (126 added, 38 deleted, 175 changed) OCL=31403 CL=31416 --- diff --git a/lib/godoc/package.html b/lib/godoc/package.html index 8d430743c4..0f1b0457d8 100644 --- a/lib/godoc/package.html +++ b/lib/godoc/package.html @@ -51,4 +51,11 @@ {.end} {.end} {.end} + {.section Bugs} +
+

Bugs

+ {.repeated section @} + {@|html-comment} + {.end} + {.end} {.end} diff --git a/lib/godoc/package.txt b/lib/godoc/package.txt index 542c2c460e..0dde78c6cf 100644 --- a/lib/godoc/package.txt +++ b/lib/godoc/package.txt @@ -58,4 +58,12 @@ TYPES {.end} {.end} {.end} +{.section Bugs} + +BUGS + +{.repeated section @} +{@} +{.end} +{.end} {.end} diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index 569e948b58..dc62d753ae 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -48,7 +48,7 @@ func parserMode() uint { func printerMode() uint { - mode := uint(0); + mode := printer.DocComments; if *optcommas { mode |= printer.OptCommas; } diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go index dc96dbb05f..4681e5dafc 100644 --- a/src/pkg/go/ast/ast.go +++ b/src/pkg/go/ast/ast.go @@ -85,15 +85,17 @@ type Decl interface { // A Comment node represents a single //-style or /*-style comment. type Comment struct { token.Position; // beginning position of the comment - Text []byte; // the comment text (without '\n' for //-style comments) - EndLine int; // the line where the comment ends + Text []byte; // comment text (excluding '\n' for //-style comments) } -// A Comments node represents a sequence of single comments +// A CommentGroup represents a sequence of single comments // with no other tokens and no empty lines between. // -type Comments []*Comment +type CommentGroup struct { + List []*Comment; + EndLine int; // line where the last comment in the group ends +} // ---------------------------------------------------------------------------- @@ -110,11 +112,11 @@ type ( // a method in an interface type, or a parameter/result declaration // in a signature. Field struct { - Doc Comments; // associated documentation; or nil + Doc *CommentGroup; // associated documentation; or nil Names []*Ident; // field/method/parameter names; nil if anonymous field Type Expr; // field/method/parameter type Tag []*StringLit; // field tag; or nil - Comment *Comment; // trailing comment on same line; or nil + Comment *CommentGroup; // trailing comments on same line; or nil }; ) @@ -670,28 +672,28 @@ type ( // An ImportSpec node represents a single package import. ImportSpec struct { - Doc Comments; // associated documentation; or nil + Doc *CommentGroup; // associated documentation; or nil Name *Ident; // local package name (including "."); or nil Path []*StringLit; // package path - Comment *Comment; // trailing comment on same line; or nil + Comment *CommentGroup; // trailing comments on same line; or nil }; // A ValueSpec node represents a constant or variable declaration // (ConstSpec or VarSpec production). ValueSpec struct { - Doc Comments; // associated documentation; or nil + Doc *CommentGroup; // associated documentation; or nil Names []*Ident; // value names Type Expr; // value type; or nil Values []Expr; // initial values; or nil - Comment *Comment; // trailing comment on same line; or nil + Comment *CommentGroup; // trailing comments on same line; or nil }; // A TypeSpec node represents a type declaration (TypeSpec production). TypeSpec struct { - Doc Comments; // associated documentation; or nil + Doc *CommentGroup; // associated documentation; or nil Name *Ident; // type name Type Expr; - Comment *Comment; // trailing comment on same line; or nil + Comment *CommentGroup; // trailing comments on same line; or nil }; ) @@ -719,7 +721,7 @@ type ( // token.VAR *ValueSpec // GenDecl struct { - Doc Comments; // associated documentation; or nil + Doc *CommentGroup; // associated documentation; or nil token.Position; // position of Tok Tok token.Token; // IMPORT, CONST, TYPE, VAR Lparen token.Position; // position of '(', if any @@ -729,7 +731,7 @@ type ( // A FuncDecl node represents a function declaration. FuncDecl struct { - Doc Comments; // associated documentation; or nil + Doc *CommentGroup; // associated documentation; or nil Recv *Field; // receiver (methods); or nil (functions) Name *Ident; // function/method name Type *FuncType; // position of Func keyword, parameters and results @@ -768,9 +770,9 @@ func (d *FuncDecl) Visit(v DeclVisitor) { v.DoFuncDecl(d); } // for an entire source file. // type Program struct { - Doc Comments; // associated documentation; or nil + Doc *CommentGroup; // associated documentation; or nil token.Position; // position of "package" keyword Name *Ident; // package name Decls []Decl; // top-level declarations - Comments []*Comment; // list of unassociated comments + Comments []*CommentGroup; // list of unassociated comments } diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go index 8bb90d9955..8ecda9f9cf 100644 --- a/src/pkg/go/ast/filter.go +++ b/src/pkg/go/ast/filter.go @@ -186,6 +186,5 @@ func FilterExports(prog *Program) bool { } } prog.Decls = prog.Decls[0 : j]; - prog.Comments = nil; // remove unassociated comments return j > 0; } diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go index ac04e82f27..b6c88a0d6a 100644 --- a/src/pkg/go/doc/comment.go +++ b/src/pkg/go/doc/comment.go @@ -34,7 +34,7 @@ func makeRex(s string) *regexp.Regexp { // TODO(rsc): Cannot use var initialization for regexps, // because Regexp constructor needs threads. func setupRegexps() { - comment_markers = makeRex("^/(/|\\*) ?"); + comment_markers = makeRex("^/[/*] ?"); trailing_whitespace = makeRex("[ \t\r]+$"); comment_junk = makeRex("^[ \t]*(/\\*|\\*/)[ \t]*$"); } diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go index bbae654a50..3f90397daa 100644 --- a/src/pkg/go/doc/doc.go +++ b/src/pkg/go/doc/doc.go @@ -31,11 +31,12 @@ type typeDoc struct { type DocReader struct { name string; // package name path string; // import path - doc ast.Comments; // package documentation, if any + doc *ast.CommentGroup; // package documentation, if any consts *vector.Vector; // list of *ast.GenDecl types map[string] *typeDoc; vars *vector.Vector; // list of *ast.GenDecl funcs map[string] *ast.FuncDecl; + bugs *vector.Vector; // list of *ast.CommentGroup } @@ -49,6 +50,7 @@ func (doc *DocReader) Init(pkg, imp string) { doc.types = make(map[string] *typeDoc); doc.vars = vector.New(0); doc.funcs = make(map[string] *ast.FuncDecl); + doc.bugs = vector.New(0); } @@ -131,11 +133,18 @@ func (doc *DocReader) addDecl(decl ast.Decl) { case token.TYPE: // types are handled individually var noPos token.Position; - for i, spec := range d.Specs { + for _, spec := range d.Specs { // make a (fake) GenDecl node for this TypeSpec // (we need to do this here - as opposed to just // for printing - so we don't lose the GenDecl // documentation) + // + // TODO(gri): Consider just collecting the TypeSpec + // node (and copy in the GenDecl.doc if there is no + // doc in the TypeSpec - this is currently done in + // makeTypeDocs below). Simpler data structures, but + // would lose GenDecl documentation if the TypeSpec + // has documentation as well. s := spec.(*ast.TypeSpec); doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, noPos, []ast.Spec{s}, noPos}); } @@ -150,10 +159,25 @@ func (doc *DocReader) addDecl(decl ast.Decl) { } +func copyCommentList(list []*ast.Comment) []*ast.Comment { + copy := make([]*ast.Comment, len(list)); + for i, c := range list { + copy[i] = c; + } + return copy; +} + + +var bug_markers *regexp.Regexp; // Regexp constructor needs threads - cannot use init expression + // AddProgram adds the AST for a source file to the DocReader. // Adding the same AST multiple times is a no-op. // func (doc *DocReader) AddProgram(prog *ast.Program) { + if bug_markers == nil { + bug_markers = makeRex("^/[/*][ \t]*BUG(\\([^)]*\\))?:?[ \t]*"); + } + if doc.name != prog.Name.Value { panic("package names don't match"); } @@ -165,22 +189,39 @@ func (doc *DocReader) AddProgram(prog *ast.Program) { } // add all declarations - for i, decl := range prog.Decls { + for _, decl := range prog.Decls { doc.addDecl(decl); } + + // collect BUG(...) comments + for _, c := range prog.Comments { + text := c.List[0].Text; + m := bug_markers.Execute(string(text)); + if len(m) > 0 { + // found a BUG comment; + // push a copy of the comment w/o the BUG prefix + list := copyCommentList(c.List); + list[0].Text = text[m[1] : len(text)]; + doc.bugs.Push(&ast.CommentGroup{list, c.EndLine}); + } + } } // ---------------------------------------------------------------------------- // Conversion to external representation -func astComment(comments ast.Comments) string { - text := make([]string, len(comments)); - for i, c := range comments { - text[i] = string(c.Text); +func astComment(comment *ast.CommentGroup) string { + if comment != nil { + text := make([]string, len(comment.List)); + for i, c := range comment.List { + text[i] = string(c.Text); + } + return commentText(text); } - return commentText(text); + return ""; } + // ValueDoc is the documentation for a group of declared // values, either vars or consts. // @@ -252,7 +293,7 @@ func (p sortFuncDoc) Less(i, j int) bool { return p[i].Name < p[j].Name; } func makeFuncDocs(m map[string] *ast.FuncDecl) []*FuncDoc { d := make([]*FuncDoc, len(m)); i := 0; - for name, f := range m { + for _, f := range m { doc := new(FuncDoc); doc.Doc = astComment(f.Doc); if f.Recv != nil { @@ -296,14 +337,19 @@ func (p sortTypeDoc) Less(i, j int) bool { // NOTE(rsc): This would appear not to be correct for type ( ) // blocks, but the doc extractor above has split them into -// individual statements. +// individual declarations. func makeTypeDocs(m map[string] *typeDoc) []*TypeDoc { d := make([]*TypeDoc, len(m)); i := 0; - for name, old := range m { + for _, old := range m { typespec := old.decl.Specs[0].(*ast.TypeSpec); t := new(TypeDoc); - t.Doc = astComment(typespec.Doc); + doc := typespec.Doc; + if doc == nil { + // no doc associated with the spec, use the declaration doc, if any + doc = old.decl.Doc; + } + t.Doc = astComment(doc); t.Type = typespec; t.Factories = makeFuncDocs(old.factories); t.Methods = makeFuncDocs(old.methods); @@ -317,6 +363,15 @@ func makeTypeDocs(m map[string] *typeDoc) []*TypeDoc { } +func makeBugDocs(v *vector.Vector) []string { + d := make([]string, v.Len()); + for i := 0; i < v.Len(); i++ { + d[i] = astComment(v.At(i).(*ast.CommentGroup)); + } + return d; +} + + // PackageDoc is the documentation for an entire package. // type PackageDoc struct { @@ -327,6 +382,7 @@ type PackageDoc struct { Types []*TypeDoc; Vars []*ValueDoc; Funcs []*FuncDoc; + Bugs []string; } @@ -341,6 +397,7 @@ func (doc *DocReader) Doc() *PackageDoc { p.Vars = makeValueDocs(doc.vars); p.Types = makeTypeDocs(doc.types); p.Funcs = makeFuncDocs(doc.funcs); + p.Bugs = makeBugDocs(doc.bugs); return p; } @@ -351,8 +408,8 @@ func (doc *DocReader) Doc() *PackageDoc { // Does s look like a regular expression? func isRegexp(s string) bool { metachars := ".(|)*+?^$[]"; - for i, c := range s { - for j, m := range metachars { + for _, c := range s { + for _, m := range metachars { if c == m { return true } @@ -363,7 +420,7 @@ func isRegexp(s string) bool { func match(s string, a []string) bool { - for i, t := range a { + for _, t := range a { if isRegexp(t) { if matched, err := regexp.Match(t, s); matched { return true; @@ -378,10 +435,10 @@ func match(s string, a []string) bool { func matchDecl(d *ast.GenDecl, names []string) bool { - for i, d := range d.Specs { + for _, d := range d.Specs { switch v := d.(type) { case *ast.ValueSpec: - for j, name := range v.Names { + for _, name := range v.Names { if match(name.Value, names) { return true; } @@ -398,7 +455,7 @@ func matchDecl(d *ast.GenDecl, names []string) bool { func filterValueDocs(a []*ValueDoc, names []string) []*ValueDoc { w := 0; - for i, vd := range a { + for _, vd := range a { if matchDecl(vd.Decl, names) { a[w] = vd; w++; @@ -410,7 +467,7 @@ func filterValueDocs(a []*ValueDoc, names []string) []*ValueDoc { func filterFuncDocs(a []*FuncDoc, names []string) []*FuncDoc { w := 0; - for i, fd := range a { + for _, fd := range a { if match(fd.Name, names) { a[w] = fd; w++; @@ -422,7 +479,7 @@ func filterFuncDocs(a []*FuncDoc, names []string) []*FuncDoc { func filterTypeDocs(a []*TypeDoc, names []string) []*TypeDoc { w := 0; - for i, td := range a { + for _, td := range a { match := false; if matchDecl(td.Decl, names) { match = true; diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go index 7c7f8c32bd..c25d5f4cbe 100644 --- a/src/pkg/go/parser/parser.go +++ b/src/pkg/go/parser/parser.go @@ -60,9 +60,16 @@ func (p ErrorList) String() string { } -type interval struct { - beg, end int; -} + +// Names to index the parser's commentIndex array. +const ( + leading = iota; // index of the leading comments entry + trailing; // index of the trailing comments entry +) + + +// Initial value for parser.commentsIndex. +var noIndex = [2]int{-1, -1}; // The parser structure holds the parser's internal state. @@ -76,10 +83,8 @@ type parser struct { indent uint; // indentation used for tracing output // Comments - // (comment indices and intervals index the comments vector) - comments vector.Vector; // list of collected, unassociated comments - lastComment int; // index of last trailing comment - lastDoc interval; // last interval of consequtive free-standing comments + comments vector.Vector; // list of collected, unassociated comment groups + commentsIndex [2]int; // comments indexes of last leading/trailing comment group; or -1 // Next token pos token.Position; // token position @@ -150,15 +155,11 @@ func (p *parser) next0() { } -// Consume a comment, add it to the parser's comment list, -// and return the line on which the comment ends. -// -func (p *parser) consumeComment() int { - // For /*-style comments, the comment may end on a different line. - // Scan the comment for '\n' chars and adjust the end line accordingly. - // (Note that the position of the next token may be even further down - // as there may be more whitespace lines after the comment.) - endline := p.pos.Line; +// Consume a comment and return it and the line on which it ends. +func (p *parser) consumeComment() (comment *ast.Comment, endline int) { + // /*-style comments may end on a different line than where they start. + // Scan the comment for '\n' chars and adjust endline accordingly. + endline = p.pos.Line; if p.lit[1] == '*' { for _, b := range p.lit { if b == '\n' { @@ -166,102 +167,99 @@ func (p *parser) consumeComment() int { } } } - p.comments.Push(&ast.Comment{p.pos, p.lit, endline}); + + comment = &ast.Comment{p.pos, p.lit}; p.next0(); - return endline; + return comment, endline; } -// Consume a group of adjacent comments and return the interval of -// indices into the parser's comment list. An empty line or non-comment -// token terminates a comment group. +// Consume a group of adjacent comments, add it to the parser's +// comments list, and return the line of which the last comment +// in the group ends. An empty line or non-comment token terminates +// a comment group. // -func (p *parser) consumeCommentGroup() interval { - beg := p.comments.Len(); +func (p *parser) consumeCommentGroup() int { + list := vector.New(0); endline := p.pos.Line; for p.tok == token.COMMENT && endline+1 >= p.pos.Line { - endline = p.consumeComment(); + var comment *ast.Comment; + comment, endline = p.consumeComment(); + list.Push(comment); } - end := p.comments.Len(); - return interval{beg, end}; -} + // convert list + group := make([]*ast.Comment, list.Len()); + for i := 0; i < list.Len(); i++ { + group[i] = list.At(i).(*ast.Comment); + } -var empty interval; + p.comments.Push(&ast.CommentGroup{group, endline}); + return endline; +} -// Advance to the next non-comment token. -func (p *parser) next() { - p.lastComment = -1; - p.lastDoc = empty; - line := p.pos.Line; +// Advance to the next non-comment token. In the process, collect +// any comment groups encountered, and remember the last leading +// and trailing comments. +// +// A leading comment is a comment group that starts and ends in a +// line without any other tokens and that is followed by a non-comment +// token on the line immediately after the comment group. +// +// A trailing comment is a comment group that follows a non-comment +// token on the same line, and that has no tokens after it on the line +// where it ends. +// +// Leading and trailing comments may be considered documentation +// that is stored in the AST. In that case they are removed from +// the parser's list of unassociated comments (via getComment). +// +func (p *parser) next() { + p.commentsIndex = noIndex; + line := p.pos.Line; // current line p.next0(); if p.tok == token.COMMENT { - // the first comment may be a trailing comment if p.pos.Line == line { - // comment is on same line as previous token; - // it is not considered part of a free-standing comment group - p.consumeComment(); - if p.pos.Line != line { - // the next token is on a different line, thus - // the last comment is a trailing comment - p.lastComment = p.comments.Len() - 1; + // The comment is on same line as previous token; it + // cannot be a leading comment but may be a trailing + // comment. + endline := p.consumeCommentGroup(); + if p.pos.Line != endline { + // The next token is on a different line, thus + // the last comment group is a trailing comment. + p.commentsIndex[trailing] = p.comments.Len() - 1; } } - // consume any successor comments - group := empty; + // consume successor comments, if any + endline := -1; for p.tok == token.COMMENT { - group = p.consumeCommentGroup(); + endline = p.consumeCommentGroup(); } - if group.end > 0 && p.comments.At(group.end - 1).(*ast.Comment).EndLine + 1 == p.pos.Line { - // there is a comment group and the next token is following on the - // line immediately after the group, thus the group may be used as - // documentation - p.lastDoc = group; + if endline >= 0 && endline+1 == p.pos.Line { + // The next token is following on the line immediately after the + // comment group, thus the last comment group is a leading comment. + p.commentsIndex[leading] = p.comments.Len() - 1; } } } -// Get current trailing comment, if any. -func (p *parser) getComment() *ast.Comment { - i := p.lastComment; - if i < 0 { - // no last comment - return nil; +// Get leading/trailing comment group, if any. +func (p *parser) getComment(kind int) *ast.CommentGroup { + i := p.commentsIndex[kind]; + if i >= 0 { + // get comment and remove if from the list of unassociated comment groups + c := p.comments.At(i).(*ast.CommentGroup); + p.comments.Set(i, nil); // clear entry + p.commentsIndex[kind] = -1; // comment was consumed + return c; } - - // get comment and remove it from the general list - c := p.comments.At(i).(*ast.Comment); - p.comments.Set(i, nil); // clear entry - p.lastComment = -1; - - return c; -} - - -// Get current documentation comment group, if any. -func (p *parser) getDoc() ast.Comments { - doc := p.lastDoc; - n := doc.end - doc.beg; - if n == 0 { - // no last comment group - return nil; - } - - // get comment group and remove if from the general list - c := make(ast.Comments, n); - for i := 0; i < n; i++ { - c[i] = p.comments.At(doc.beg + i).(*ast.Comment); - p.comments.Set(doc.beg + i, nil); // clear entry - } - p.lastDoc = empty; - - return c; + return nil; } @@ -453,7 +451,7 @@ func (p *parser) parseFieldDecl() *ast.Field { defer un(trace(p, "FieldDecl")); } - doc := p.getDoc(); + doc := p.getComment(leading); // a list of identifiers looks like a list of type names list := vector.New(0); @@ -514,9 +512,9 @@ func (p *parser) parseStructType() *ast.StructType { list.Push(f); if p.tok == token.SEMICOLON { p.next(); - f.Comment = p.getComment(); + f.Comment = p.getComment(trailing); } else { - f.Comment = p.getComment(); + f.Comment = p.getComment(trailing); break; } } @@ -698,7 +696,7 @@ func (p *parser) parseMethodSpec() *ast.Field { defer un(trace(p, "MethodSpec")); } - doc := p.getDoc(); + doc := p.getComment(leading); var idents []*ast.Ident; var typ ast.Expr; x := p.parseQualifiedIdent(); @@ -1695,22 +1693,22 @@ func (p *parser) parseStatement() ast.Stmt { // ---------------------------------------------------------------------------- // Declarations -type parseSpecFunction func(p *parser, doc ast.Comments, getSemi bool) (spec ast.Spec, gotSemi bool) +type parseSpecFunction func(p *parser, doc *ast.CommentGroup, getSemi bool) (spec ast.Spec, gotSemi bool) // Consume semicolon if there is one and getSemi is set, and get any trailing comment. // Return the comment if any and indicate if a semicolon was consumed. // -func (p *parser) parseComment(getSemi bool) (comment *ast.Comment, gotSemi bool) { +func (p *parser) parseComment(getSemi bool) (comment *ast.CommentGroup, gotSemi bool) { if getSemi && p.tok == token.SEMICOLON { p.next(); gotSemi = true; } - return p.getComment(), gotSemi; + return p.getComment(trailing), gotSemi; } -func parseImportSpec(p *parser, doc ast.Comments, getSemi bool) (spec ast.Spec, gotSemi bool) { +func parseImportSpec(p *parser, doc *ast.CommentGroup, getSemi bool) (spec ast.Spec, gotSemi bool) { if p.trace { defer un(trace(p, "ImportSpec")); } @@ -1736,7 +1734,7 @@ func parseImportSpec(p *parser, doc ast.Comments, getSemi bool) (spec ast.Spec, } -func parseConstSpec(p *parser, doc ast.Comments, getSemi bool) (spec ast.Spec, gotSemi bool) { +func parseConstSpec(p *parser, doc *ast.CommentGroup, getSemi bool) (spec ast.Spec, gotSemi bool) { if p.trace { defer un(trace(p, "ConstSpec")); } @@ -1754,7 +1752,7 @@ func parseConstSpec(p *parser, doc ast.Comments, getSemi bool) (spec ast.Spec, g } -func parseTypeSpec(p *parser, doc ast.Comments, getSemi bool) (spec ast.Spec, gotSemi bool) { +func parseTypeSpec(p *parser, doc *ast.CommentGroup, getSemi bool) (spec ast.Spec, gotSemi bool) { if p.trace { defer un(trace(p, "TypeSpec")); } @@ -1767,7 +1765,7 @@ func parseTypeSpec(p *parser, doc ast.Comments, getSemi bool) (spec ast.Spec, go } -func parseVarSpec(p *parser, doc ast.Comments, getSemi bool) (spec ast.Spec, gotSemi bool) { +func parseVarSpec(p *parser, doc *ast.CommentGroup, getSemi bool) (spec ast.Spec, gotSemi bool) { if p.trace { defer un(trace(p, "VarSpec")); } @@ -1790,7 +1788,7 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction, getSemi defer un(trace(p, keyword.String() + "Decl")); } - doc := p.getDoc(); + doc := p.getComment(leading); pos := p.expect(keyword); var lparen, rparen token.Position; list := vector.New(0); @@ -1798,7 +1796,7 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction, getSemi lparen = p.pos; p.next(); for p.tok != token.RPAREN && p.tok != token.EOF { - doc := p.getDoc(); + doc := p.getComment(leading); spec, semi := f(p, doc, true); // consume semicolon if any list.Push(spec); if !semi { @@ -1814,7 +1812,7 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction, getSemi p.optSemi = true; } } else { - spec, semi := f(p, doc, getSemi); + spec, semi := f(p, nil, getSemi); list.Push(spec); gotSemi = semi; } @@ -1863,7 +1861,7 @@ func (p *parser) parseFunctionDecl() *ast.FuncDecl { defer un(trace(p, "FunctionDecl")); } - doc := p.getDoc(); + doc := p.getComment(leading); pos := p.expect(token.FUNC); var recv *ast.Field; @@ -1945,7 +1943,7 @@ func (p *parser) parsePackage() *ast.Program { } // package clause - comment := p.getDoc(); + comment := p.getComment(leading); pos := p.expect(token.PACKAGE); ident := p.parseIdent(); var decls []ast.Decl; @@ -1985,10 +1983,10 @@ func (p *parser) parsePackage() *ast.Program { } } // 2) convert the remaining comments - comments := make([]*ast.Comment, n); + comments := make([]*ast.CommentGroup, n); for i, j := 0, 0; i < p.comments.Len(); i++ { if p.comments.At(i) != nil { - comments[j] = p.comments.At(i).(*ast.Comment); + comments[j] = p.comments.At(i).(*ast.CommentGroup); j++; } } @@ -2046,6 +2044,7 @@ func (p *parser) init(src interface{}, mode uint) os.Error { p.mode = mode; p.trace = mode & Trace != 0; // for convenience (p.trace is used frequently) p.comments.Init(0); + p.commentsIndex = noIndex; p.next(); return nil; diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go index f91a14c05a..e09387cf7f 100644 --- a/src/pkg/go/printer/printer.go +++ b/src/pkg/go/printer/printer.go @@ -31,7 +31,7 @@ type printer struct { output io.Writer; mode uint; errors chan os.Error; - comments ast.Comments; // list of unassociated comments; or nil + comments []*ast.CommentGroup; // list of unassociated comments; or nil // current state (changes during printing) written int; // number of bytes written @@ -40,8 +40,8 @@ type printer struct { pos token.Position; // output position (possibly estimated) in "AST space" // comments - cindex int; // the current comment index - cpos token.Position; // the position of the next comment + cindex int; // the current comment group index + cpos token.Position; // the position of the next comment group } @@ -53,14 +53,14 @@ func (p *printer) hasComment(pos token.Position) bool { func (p *printer) nextComment() { p.cindex++; if p.comments != nil && p.cindex < len(p.comments) && p.comments[p.cindex] != nil { - p.cpos = p.comments[p.cindex].Pos(); + p.cpos = p.comments[p.cindex].List[0].Pos(); } else { p.cpos = token.Position{1<<30, 1<<30, 1}; // infinite } } -func (p *printer) setComments(comments ast.Comments) { +func (p *printer) setComments(comments []*ast.CommentGroup) { p.comments = comments; p.cindex = -1; p.nextComment(); @@ -125,6 +125,8 @@ func (p *printer) write(data []byte) { } +// TODO(gri) Enable this code to intersperse comments +/* // Reduce contiguous sequences of '\t' in a []byte to a single '\t'. func untabify(src []byte) []byte { dst := make([]byte, len(src)); @@ -149,12 +151,13 @@ func (p *printer) adjustSpacingAndMergeComments() { // - add extra newlines if so indicated by source positions } } +*/ func (p *printer) print(args ...) { v := reflect.NewValue(args).(*reflect.StructValue); for i := 0; i < v.NumField(); i++ { - p.adjustSpacingAndMergeComments(); + //p.adjustSpacingAndMergeComments(); // TODO(gri) enable to intersperse comments f := v.Field(i); switch x := f.Interface().(type) { case int: @@ -187,24 +190,35 @@ func (p *printer) optSemis() bool { } -func (p *printer) comment(c *ast.Comment) { - if c != nil { - text := c.Text; - if text[1] == '/' { - // //-style comment - dont print the '\n' - // TODO scanner should probably not include the '\n' in this case - text = text[0 : len(text)-1]; +// Print a list of individual comments. +func (p *printer) commentList(list []*ast.Comment) { + for i, c := range list { + t := c.Text; + p.print(c.Pos(), t); + if t[1] == '/' && i+1 < len(list) { + //-style comment which is not at the end; print a newline + p.print(newline); } - p.print(tab, c.Pos(), text); // tab-separated trailing comment } } -func (p *printer) doc(d ast.Comments) { - if p.mode & DocComments != 0 { - for _, c := range d { - p.print(c.Pos(), c.Text); - } +// Print a leading comment followed by a newline. +func (p *printer) leadingComment(d *ast.CommentGroup) { + if p.mode & DocComments != 0 && d != nil { + p.commentList(d.List); + p.print(newline); + } +} + + +// Print a tab followed by a trailing comment. +// A newline must be printed afterwards since +// the comment may be a //-style comment. +func (p *printer) trailingComment(d *ast.CommentGroup) { + if d != nil { + p.print(tab); + p.commentList(d.List); } } @@ -286,13 +300,13 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok p.print(blank, lbrace, token.LBRACE, +1, newline); var lastWasAnon bool; // true if the previous line was an anonymous field - var lastComment *ast.Comment; // the comment from the previous line + var lastComment *ast.CommentGroup; // the comment from the previous line for i, f := range list { // at least one visible identifier or anonymous field isAnon := len(f.Names) == 0; if i > 0 { p.print(token.SEMICOLON); - p.comment(lastComment); + p.trailingComment(lastComment); if lastWasAnon == isAnon { // previous and current line have same structure; // continue with existing columns @@ -307,7 +321,7 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok } } - p.doc(f.Doc); + p.leadingComment(f.Doc); if !isAnon { p.identList(f.Names); p.print(tab); @@ -336,7 +350,7 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok if p.optSemis() { p.print(token.SEMICOLON); } - p.comment(lastComment); + p.trailingComment(lastComment); p.print(-1, newline, rbrace, token.RBRACE); @@ -521,7 +535,7 @@ func (p *printer) expr(x ast.Expr) bool { // ---------------------------------------------------------------------------- // Statements -func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) +func (p *printer) decl(decl ast.Decl) (comment *ast.CommentGroup, optSemi bool) // Print the statement list indented, but without a newline after the last statement. func (p *printer) stmtList(list []ast.Stmt) { @@ -607,14 +621,14 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) { p.print("BadStmt"); case *ast.DeclStmt: - var comment *ast.Comment; + var comment *ast.CommentGroup; comment, optSemi = p.decl(s.Decl); if comment != nil { // Trailing comments of declarations in statement lists // are not associated with the declaration in the parser; // this case should never happen. Print anyway to continue // gracefully. - p.comment(comment); + p.trailingComment(comment); p.print(newline); } @@ -768,10 +782,10 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) { // Returns trailing comment, if any, and whether a separating semicolon is optional. // -func (p *printer) spec(spec ast.Spec) (comment *ast.Comment, optSemi bool) { +func (p *printer) spec(spec ast.Spec) (comment *ast.CommentGroup, optSemi bool) { switch s := spec.(type) { case *ast.ImportSpec: - p.doc(s.Doc); + p.leadingComment(s.Doc); if s.Name != nil { p.expr(s.Name); } @@ -780,7 +794,7 @@ func (p *printer) spec(spec ast.Spec) (comment *ast.Comment, optSemi bool) { comment = s.Comment; case *ast.ValueSpec: - p.doc(s.Doc); + p.leadingComment(s.Doc); p.identList(s.Names); if s.Type != nil { p.print(blank); // TODO switch to tab? (indent problem with structs) @@ -794,7 +808,7 @@ func (p *printer) spec(spec ast.Spec) (comment *ast.Comment, optSemi bool) { comment = s.Comment; case *ast.TypeSpec: - p.doc(s.Doc); + p.leadingComment(s.Doc); p.expr(s.Name); p.print(blank); // TODO switch to tab? (but indent problem with structs) optSemi = p.expr(s.Type); @@ -809,13 +823,13 @@ func (p *printer) spec(spec ast.Spec) (comment *ast.Comment, optSemi bool) { // Returns true if a separating semicolon is optional. -func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) { +func (p *printer) decl(decl ast.Decl) (comment *ast.CommentGroup, optSemi bool) { switch d := decl.(type) { case *ast.BadDecl: p.print(d.Pos(), "BadDecl"); case *ast.GenDecl: - p.doc(d.Doc); + p.leadingComment(d.Doc); p.print(d.Pos(), d.Tok, blank); if d.Lparen.IsValid() { @@ -826,7 +840,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) { for i, s := range d.Specs { if i > 0 { p.print(token.SEMICOLON); - p.comment(comment); + p.trailingComment(comment); p.print(newline); } comment, optSemi = p.spec(s); @@ -834,7 +848,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) { if p.optSemis() { p.print(token.SEMICOLON); } - p.comment(comment); + p.trailingComment(comment); p.print(-1, newline); } p.print(d.Rparen, token.RPAREN); @@ -847,7 +861,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) { } case *ast.FuncDecl: - p.doc(d.Doc); + p.leadingComment(d.Doc); p.print(d.Pos(), token.FUNC, blank); if recv := d.Recv; recv != nil { // method: print receiver @@ -880,11 +894,9 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.Comment, optSemi bool) { // Programs func (p *printer) program(prog *ast.Program) { - // set unassociated comments - // TODO enable this once comments are properly interspersed - // p.setComments(prog.Comments); + p.setComments(prog.Comments); // unassociated comments - p.doc(prog.Doc); + p.leadingComment(prog.Doc); p.print(prog.Pos(), token.PACKAGE, blank); p.expr(prog.Name); @@ -894,7 +906,7 @@ func (p *printer) program(prog *ast.Program) { if p.optSemis() { p.print(token.SEMICOLON); } - p.comment(comment); + p.trailingComment(comment); } p.print(newline); @@ -922,7 +934,7 @@ func Fprint(output io.Writer, node interface{}, mode uint) (int, os.Error) { p.stmt(n); case ast.Decl: comment, _ := p.decl(n); - p.comment(comment); + p.trailingComment(comment); // no newline at end case *ast.Program: p.program(n); default: diff --git a/src/pkg/go/printer/printer_test.go b/src/pkg/go/printer/printer_test.go index 8390909294..dc42098e5e 100644 --- a/src/pkg/go/printer/printer_test.go +++ b/src/pkg/go/printer/printer_test.go @@ -61,7 +61,7 @@ func check(t *testing.T, source, golden string, exports bool) { // format source var buf bytes.Buffer; w := tabwriter.NewWriter(&buf, tabwidth, padding, tabchar, 0); - Fprint(w, prog, 0); + Fprint(w, prog, DocComments); w.Flush(); res := buf.Data(); diff --git a/src/pkg/go/printer/testdata/golden1.go b/src/pkg/go/printer/testdata/golden1.go index f8fb9451b1..56d07ce5b3 100644 --- a/src/pkg/go/printer/testdata/golden1.go +++ b/src/pkg/go/printer/testdata/golden1.go @@ -9,6 +9,7 @@ const ( c2 // c2 ) +// The T type. type T struct { a, b, c int // 3 fields } diff --git a/src/pkg/go/printer/testdata/golden1.x b/src/pkg/go/printer/testdata/golden1.x index b26ce28733..273de2b403 100644 --- a/src/pkg/go/printer/testdata/golden1.x +++ b/src/pkg/go/printer/testdata/golden1.x @@ -1,3 +1,4 @@ package main +// The T type. type T struct diff --git a/src/pkg/go/printer/testdata/source1.go b/src/pkg/go/printer/testdata/source1.go index 99567ec1e2..0798540156 100644 --- a/src/pkg/go/printer/testdata/source1.go +++ b/src/pkg/go/printer/testdata/source1.go @@ -9,6 +9,7 @@ const ( ) +// The T type. type T struct { a, b, c int // 3 fields } diff --git a/src/pkg/go/scanner/scanner.go b/src/pkg/go/scanner/scanner.go index 83497790eb..795d56f8ba 100644 --- a/src/pkg/go/scanner/scanner.go +++ b/src/pkg/go/scanner/scanner.go @@ -141,7 +141,8 @@ func (S *Scanner) scanComment(pos token.Position) { for S.ch >= 0 { S.next(); if S.ch == '\n' { - S.next(); // '\n' belongs to the comment + // '\n' is not part of the comment + // (the comment ends on the same line where it started) return; } }