//
 type CommentGroup struct {
        List []*Comment
-       Next *CommentGroup // next comment group in source order
 }
 
 
 
 // A File node represents a Go source file.
 //
+// The Comments list contains all comments in the source file in order of
+// appearance, including the comments that are pointed to from other nodes
+// via Doc and Comment fields.
+//
 type File struct {
-       Doc            *CommentGroup // associated documentation; or nil
-       token.Position               // position of "package" keyword
-       Name           *Ident        // package name
-       Decls          []Decl        // top-level declarations
-       Comments       *CommentGroup // list of all comments in the source file
+       Doc            *CommentGroup   // associated documentation; or nil
+       token.Position                 // position of "package" keyword
+       Name           *Ident          // package name
+       Decls          []Decl          // top-level declarations
+       Comments       []*CommentGroup // list of all comments in the source file
 }
 
 
 
                        return true
                }
        case *TypeSpec:
-               // TODO(gri) consider stripping forward declarations
-               //           of structs, interfaces, functions, and methods
                if s.Name.IsExported() {
                        filterType(s.Type)
                        return true
                d.Specs = filterSpecList(d.Specs)
                return len(d.Specs) > 0
        case *FuncDecl:
-               // TODO consider removing function declaration altogether if
-               //      forward declaration (i.e., if d.Body == nil) because
-               //      in that case the actual declaration will come later.
                d.Body = nil // strip body
                return d.Name.IsExported()
        }
                                }
                        }
                }
-               doc = &CommentGroup{list, nil}
+               doc = &CommentGroup{list}
        }
 
        // Collect declarations from all package files.
                }
        }
 
-       // TODO(gri) Should collect comments as well. For that the comment
-       //           list should be changed back into a []*CommentGroup,
-       //           otherwise need to modify the existing linked list.
+       // TODO(gri) Should collect comments as well.
        return &File{doc, noPos, NewIdent(pkg.Name), decls, nil}
 }
 
                for _, c := range n.List {
                        Walk(v, c)
                }
-               // TODO(gri): Keep comments in a list/vector instead
-               // of linking them via Next. Following next will lead
-               // to multiple visits and potentially n^2 behavior
-               // since Doc and Comments fields point into the global
-               // comments list.
 
        case *Field:
                walkCommentGroup(v, n.Doc)
                walkCommentGroup(v, n.Doc)
                walkIdent(v, n.Name)
                Walk(v, n.Decls)
-               walkCommentGroup(v, n.Comments)
+               for _, g := range n.Comments {
+                       Walk(v, g)
+               }
 
        case *Package:
                for _, f := range n.Files {
 
        }
 
        // collect BUG(...) comments
-       for c := src.Comments; c != nil; c = c.Next {
+       for _, c := range src.Comments {
                text := c.List[0].Text
                cstr := string(text)
                if m := bug_markers.ExecuteString(cstr); len(m) > 0 {
                                // non-empty BUG comment; collect comment without BUG prefix
                                list := copyCommentList(c.List)
                                list[0].Text = text[m[1]:]
-                               doc.bugs.Push(&ast.CommentGroup{list, nil})
+                               doc.bugs.Push(&ast.CommentGroup{list})
                        }
                }
        }
 
        indent uint // indentation used for tracing output
 
        // Comments
-       comments    *ast.CommentGroup // list of collected comments
+       comments    vector.Vector     // list of *CommentGroup
        lastComment *ast.CommentGroup // last comment in the comments list
        leadComment *ast.CommentGroup // the last lead comment
        lineComment *ast.CommentGroup // the last line comment
        }
 
        // add comment group to the comments list
-       g := &ast.CommentGroup{group, nil}
-       if p.lastComment != nil {
-               p.lastComment.Next = g
-       } else {
-               p.comments = g
-       }
+       g := &ast.CommentGroup{group}
+       p.comments.Push(g)
        p.lastComment = g
 
        return endline
                }
        }
 
-       return &ast.File{doc, pos, ident, decls, p.comments}
+       // convert comments list
+       comments := make([]*ast.CommentGroup, len(p.comments))
+       for i, x := range p.comments {
+               comments[i] = x.(*ast.CommentGroup)
+       }
+
+       return &ast.File{doc, pos, ident, decls, comments}
 }
 
 // Print a lead comment followed by a newline.
 func (p *printer) leadComment(d *ast.CommentGroup) {
        // Ignore the comment if we have comments interspersed (p.comment != nil).
-       if p.comment == nil && d != nil {
+       if p.comments == nil && d != nil {
                p.commentList(d.List)
                p.print(newline)
        }
 // the comment may be a //-style comment.
 func (p *printer) lineComment(d *ast.CommentGroup) {
        // Ignore the comment if we have comments interspersed (p.comment != nil).
-       if p.comment == nil && d != nil {
+       if p.comments == nil && d != nil {
                p.print(vtab)
                p.commentList(d.List)
        }
 // Remove this after transitioning to new semicolon syntax and
 // some reasonable grace period (12/11/09).
 func (p *printer) beforeComment(pos token.Position) token.Position {
-       if p.comment != nil {
-               p := p.comment.List[0].Position
+       if p.cindex < len(p.comments) {
+               p := p.comments[p.cindex].List[0].Position
                if !pos.IsValid() || pos.Offset > p.Offset {
                        return p
                }
        //            Remove this after transitioning to new semicolon
        //            syntax and some reasonable grace period (12/11/09).
        if p.commentBefore(pos) {
-               p.comment.List[0].Position = pos
+               p.comments[p.cindex].List[0].Position = pos
        }
 }
 
 
        // HTML support
        lastTaggedLine int // last line for which a line tag was written
 
-       // The list of comments; or nil.
-       comment *ast.CommentGroup
+       // The list of all source comments, in order of appearance.
+       comments []*ast.CommentGroup // may be nil
+       cindex   int                 // current comment index
 }
 
 
        isFirst := true
        needsLinebreak := false
        var last *ast.Comment
-       for ; p.commentBefore(next); p.comment = p.comment.Next {
-               for _, c := range p.comment.List {
+       for ; p.commentBefore(next); p.cindex++ {
+               for _, c := range p.comments[p.cindex].List {
                        p.writeCommentPrefix(c.Pos(), next, isFirst, isKeyword)
                        isFirst = false
                        p.writeComment(c)
 // before the next position in the source code.
 //
 func (p *printer) commentBefore(next token.Position) bool {
-       return p.comment != nil && p.comment.List[0].Pos().Offset < next.Offset
+       return p.cindex < len(p.comments) && p.comments[p.cindex].List[0].Pos().Offset < next.Offset
 }
 
 
                case ast.Decl:
                        p.decl(n, atTop, ignoreMultiLine)
                case *ast.File:
-                       p.comment = n.Comments
+                       p.comments = n.Comments
                        p.file(n)
                default:
                        p.errors <- os.NewError(fmt.Sprintf("printer.Fprint: unsupported node type %T", n))