]> Cypherpunks repositories - gostls13.git/commitdiff
Show BUG comments in godoc:
authorRobert Griesemer <gri@golang.org>
Thu, 9 Jul 2009 23:38:17 +0000 (16:38 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 9 Jul 2009 23:38:17 +0000 (16:38 -0700)
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

14 files changed:
lib/godoc/package.html
lib/godoc/package.txt
src/cmd/gofmt/gofmt.go
src/pkg/go/ast/ast.go
src/pkg/go/ast/filter.go
src/pkg/go/doc/comment.go
src/pkg/go/doc/doc.go
src/pkg/go/parser/parser.go
src/pkg/go/printer/printer.go
src/pkg/go/printer/printer_test.go
src/pkg/go/printer/testdata/golden1.go
src/pkg/go/printer/testdata/golden1.x
src/pkg/go/printer/testdata/source1.go
src/pkg/go/scanner/scanner.go

index 8d430743c49dfad6aea765c740a8c822da1cb6b2..0f1b0457d85ab30dee3efdd8a926e788fa3a54cf 100644 (file)
                        {.end}
                {.end}
        {.end}
+       {.section Bugs}
+               <hr />
+               <h2>Bugs</h2>
+               {.repeated section @}
+               {@|html-comment}
+               {.end}
+       {.end}
 {.end}
index 542c2c460e0422b41f2513702da0a42a6e4e274b..0dde78c6cf112e3b259769f7645b83f0ed67337c 100644 (file)
@@ -58,4 +58,12 @@ TYPES
 {.end}
 {.end}
 {.end}
+{.section Bugs}
+
+BUGS
+
+{.repeated section @}
+{@}
+{.end}
+{.end}
 {.end}
index 569e948b58095d670ed18aad4cd0ca2b26f1de08..dc62d753aec31a78ab797c0cb9548e2cc1a3da1c 100644 (file)
@@ -48,7 +48,7 @@ func parserMode() uint {
 
 
 func printerMode() uint {
-       mode := uint(0);
+       mode := printer.DocComments;
        if *optcommas {
                mode |= printer.OptCommas;
        }
index dc96dbb05f1267b5dbd315c29339d923d440ae51..4681e5dafccd4ae1b87ee828e967e55136190739 100644 (file)
@@ -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
 }
index 8bb90d995598680e3be7f37f8b9fe83820bec91c..8ecda9f9cf4393b45b40b3c45fa8ff05eda4c7af 100644 (file)
@@ -186,6 +186,5 @@ func FilterExports(prog *Program) bool {
                }
        }
        prog.Decls = prog.Decls[0 : j];
-       prog.Comments = nil;  // remove unassociated comments
        return j > 0;
 }
index ac04e82f274da83ff35d8a60ef9c66b99f713fb7..b6c88a0d6a5635d7edb1fb9d84d81dc57fcc9264 100644 (file)
@@ -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]*$");
 }
index bbae654a50412a0c32073e07738d37cdfb73eec8..3f90397daa641d1f0df5c8d6e4996ee40888fc39 100644 (file)
@@ -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;
index 7c7f8c32bd8b94164e4bdbab132411c95ae548f0..c25d5f4cbe3f71c055b6d11f74fd3f084e08fc6d 100644 (file)
@@ -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;
index f91a14c05a3ad59067dea68578b6d131fc6e52a4..e09387cf7f086d430046133a08c5972289fe3046 100644 (file)
@@ -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:
index 8390909294ad38f01dfc9fd8afcadcdf411bf705..dc42098e5e1c99cfd0bec237a196e37ca1f4bdc9 100644 (file)
@@ -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();
 
index f8fb9451b1dc23f9317d23236092e1b67cdcd412..56d07ce5b30328854027606cddef609a887509dd 100644 (file)
@@ -9,6 +9,7 @@ const (
        c2      // c2
 )
 
+// The T type.
 type T struct {
        a, b, c int     // 3 fields
 }
index b26ce287333549e11a4f7b6caaac789f426d2311..273de2b403c02ce92259ef0f33a6a24b6830e7ff 100644 (file)
@@ -1,3 +1,4 @@
 package main
 
+// The T type.
 type T struct
index 99567ec1e2e962e0985d13db845dd1aacf8fd776..0798540156250be0621237cad35c2d9b4a489efd 100644 (file)
@@ -9,6 +9,7 @@ const (
 )
 
 
+// The T type.
 type T struct {
        a, b, c int  // 3 fields
 }
index 83497790eb4a58e600ff1a626c6d597ce2f243b2..795d56f8ba18ce0e2b5379821ce5de642abe1b4b 100644 (file)
@@ -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;
                        }
                }