mirror of
https://github.com/golang/go
synced 2024-11-22 00:04:41 -07:00
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
This commit is contained in:
parent
ec23467e65
commit
38e7fddc21
@ -51,4 +51,11 @@
|
||||
{.end}
|
||||
{.end}
|
||||
{.end}
|
||||
{.section Bugs}
|
||||
<hr />
|
||||
<h2>Bugs</h2>
|
||||
{.repeated section @}
|
||||
{@|html-comment}
|
||||
{.end}
|
||||
{.end}
|
||||
{.end}
|
||||
|
@ -58,4 +58,12 @@ TYPES
|
||||
{.end}
|
||||
{.end}
|
||||
{.end}
|
||||
{.section Bugs}
|
||||
|
||||
BUGS
|
||||
|
||||
{.repeated section @}
|
||||
{@}
|
||||
{.end}
|
||||
{.end}
|
||||
{.end}
|
||||
|
@ -48,7 +48,7 @@ func parserMode() uint {
|
||||
|
||||
|
||||
func printerMode() uint {
|
||||
mode := uint(0);
|
||||
mode := printer.DocComments;
|
||||
if *optcommas {
|
||||
mode |= printer.OptCommas;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -186,6 +186,5 @@ func FilterExports(prog *Program) bool {
|
||||
}
|
||||
}
|
||||
prog.Decls = prog.Decls[0 : j];
|
||||
prog.Comments = nil; // remove unassociated comments
|
||||
return j > 0;
|
||||
}
|
||||
|
@ -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]*$");
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 comment, endline;
|
||||
}
|
||||
|
||||
|
||||
// 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() int {
|
||||
list := vector.New(0);
|
||||
endline := p.pos.Line;
|
||||
for p.tok == token.COMMENT && endline+1 >= p.pos.Line {
|
||||
var comment *ast.Comment;
|
||||
comment, endline = p.consumeComment();
|
||||
list.Push(comment);
|
||||
}
|
||||
|
||||
// convert list
|
||||
group := make([]*ast.Comment, list.Len());
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
group[i] = list.At(i).(*ast.Comment);
|
||||
}
|
||||
|
||||
p.comments.Push(&ast.CommentGroup{group, endline});
|
||||
return 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.
|
||||
// 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) consumeCommentGroup() interval {
|
||||
beg := p.comments.Len();
|
||||
endline := p.pos.Line;
|
||||
for p.tok == token.COMMENT && endline+1 >= p.pos.Line {
|
||||
endline = p.consumeComment();
|
||||
}
|
||||
end := p.comments.Len();
|
||||
return interval{beg, end};
|
||||
}
|
||||
|
||||
|
||||
var empty interval;
|
||||
|
||||
// Advance to the next non-comment token.
|
||||
func (p *parser) next() {
|
||||
p.lastComment = -1;
|
||||
p.lastDoc = empty;
|
||||
|
||||
line := p.pos.Line;
|
||||
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;
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
|
||||
|
1
src/pkg/go/printer/testdata/golden1.go
vendored
1
src/pkg/go/printer/testdata/golden1.go
vendored
@ -9,6 +9,7 @@ const (
|
||||
c2 // c2
|
||||
)
|
||||
|
||||
// The T type.
|
||||
type T struct {
|
||||
a, b, c int // 3 fields
|
||||
}
|
||||
|
1
src/pkg/go/printer/testdata/golden1.x
vendored
1
src/pkg/go/printer/testdata/golden1.x
vendored
@ -1,3 +1,4 @@
|
||||
package main
|
||||
|
||||
// The T type.
|
||||
type T struct
|
||||
|
1
src/pkg/go/printer/testdata/source1.go
vendored
1
src/pkg/go/printer/testdata/source1.go
vendored
@ -9,6 +9,7 @@ const (
|
||||
)
|
||||
|
||||
|
||||
// The T type.
|
||||
type T struct {
|
||||
a, b, c int // 3 fields
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user