Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1464)

Delta Between Two Patch Sets: src/pkg/go/doc/doc.go

Issue 4291070: code review 4291070: go/scanner: return literal as string instead of []byte (Closed)
Left Patch Set: diff -r 6659c68c1d45 https://go.googlecode.com/hg/ Created 13 years ago
Right Patch Set: diff -r 4073ecdfc054 https://go.googlecode.com/hg/ Created 13 years ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
LEFTRIGHT
1 ÿ// Copyright 2009 The Go Authors. All rights reserved.ÿ 1 // Copyright 2009 The Go Authors. All rights reserved.
2 ÿ// Use of this source code is governed by a BSD-styleÿ 2 // Use of this source code is governed by a BSD-style
3 ÿ// license that can be found in the LICENSE file.ÿ 3 // license that can be found in the LICENSE file.
4 4
5 ÿ// The doc package extracts source code documentation from a Go AST.ÿ 5 // The doc package extracts source code documentation from a Go AST.
6 package doc 6 package doc
7 7
8 import ( 8 import (
9 "go/ast" 9 "go/ast"
10 "go/token" 10 "go/token"
11 "regexp" 11 "regexp"
12 "sort" 12 "sort"
13 ) 13 )
14 14
15 15
16 ÿ// ---------------------------------------------------------------------------- ÿ 16 // ----------------------------------------------------------------------------
17 17
18 type typeDoc struct { 18 type typeDoc struct {
19 » ÿ// len(decl.Specs) == 1, and the element type is *ast.TypeSpecÿ 19 » // len(decl.Specs) == 1, and the element type is *ast.TypeSpec
20 » ÿ// if the type declaration hasn't been seen yet, decl is nilÿ 20 » // if the type declaration hasn't been seen yet, decl is nil
21 decl *ast.GenDecl 21 decl *ast.GenDecl
22 » ÿ// values, factory functions, and methods associated with the typeÿ 22 » // values, factory functions, and methods associated with the type
23 » values []*ast.GenDecl ÿ// consts and varsÿ 23 » values []*ast.GenDecl // consts and vars
24 factories map[string]*ast.FuncDecl 24 factories map[string]*ast.FuncDecl
25 methods map[string]*ast.FuncDecl 25 methods map[string]*ast.FuncDecl
26 } 26 }
27 27
28 28
29 ÿ// docReader accumulates documentation for a single package.ÿ 29 // docReader accumulates documentation for a single package.
30 ÿ// It modifies the AST: Comments (declaration documentation)ÿ 30 // It modifies the AST: Comments (declaration documentation)
31 ÿ// that have been collected by the DocReader are set to nilÿ 31 // that have been collected by the DocReader are set to nil
32 ÿ// in the respective AST nodes so that they are not printedÿ 32 // in the respective AST nodes so that they are not printed
33 ÿ// twice (once when printing the documentation and once whenÿ 33 // twice (once when printing the documentation and once when
34 ÿ// printing the corresponding AST node).ÿ 34 // printing the corresponding AST node).
35 ÿ//ÿ 35 //
36 type docReader struct { 36 type docReader struct {
37 » doc *ast.CommentGroup ÿ// package documentation, if anyÿ 37 » doc *ast.CommentGroup // package documentation, if any
38 pkgName string 38 pkgName string
39 » values []*ast.GenDecl ÿ// consts and varsÿ 39 » values []*ast.GenDecl // consts and vars
40 types map[string]*typeDoc 40 types map[string]*typeDoc
41 funcs map[string]*ast.FuncDecl 41 funcs map[string]*ast.FuncDecl
42 bugs []*ast.CommentGroup 42 bugs []*ast.CommentGroup
43 } 43 }
44 44
45 45
46 func (doc *docReader) init(pkgName string) { 46 func (doc *docReader) init(pkgName string) {
47 doc.pkgName = pkgName 47 doc.pkgName = pkgName
48 doc.types = make(map[string]*typeDoc) 48 doc.types = make(map[string]*typeDoc)
49 doc.funcs = make(map[string]*ast.FuncDecl) 49 doc.funcs = make(map[string]*ast.FuncDecl)
50 } 50 }
51 51
52 52
53 func (doc *docReader) addDoc(comments *ast.CommentGroup) { 53 func (doc *docReader) addDoc(comments *ast.CommentGroup) {
54 if doc.doc == nil { 54 if doc.doc == nil {
55 » » ÿ// common case: just one package commentÿ 55 » » // common case: just one package comment
56 doc.doc = comments 56 doc.doc = comments
57 return 57 return
58 } 58 }
59 59
60 » ÿ// More than one package comment: Usually there will be onlyÿ 60 » // More than one package comment: Usually there will be only
61 » ÿ// one file with a package comment, but it's better to collectÿ 61 » // one file with a package comment, but it's better to collect
62 » ÿ// all comments than drop them on the floor.ÿ 62 » // all comments than drop them on the floor.
63 » ÿ// (This code isn't particularly clever - no amortized doubling isÿ 63 » // (This code isn't particularly clever - no amortized doubling is
64 » ÿ// used - but this situation occurs rarely and is not time-critical.)ÿ 64 » // used - but this situation occurs rarely and is not time-critical.)
65 n1 := len(doc.doc.List) 65 n1 := len(doc.doc.List)
66 n2 := len(comments.List) 66 n2 := len(comments.List)
67 » list := make([]*ast.Comment, n1+1+n2) ÿ// + 1 for separator lineÿ 67 » list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line
68 copy(list, doc.doc.List) 68 copy(list, doc.doc.List)
69 » list[n1] = &ast.Comment{token.NoPos, "//"} ÿ// separator lineÿ 69 » list[n1] = &ast.Comment{token.NoPos, "//"} // separator line
70 copy(list[n1+1:], comments.List) 70 copy(list[n1+1:], comments.List)
71 doc.doc = &ast.CommentGroup{list} 71 doc.doc = &ast.CommentGroup{list}
72 } 72 }
73 73
74 74
75 func (doc *docReader) addType(decl *ast.GenDecl) { 75 func (doc *docReader) addType(decl *ast.GenDecl) {
76 spec := decl.Specs[0].(*ast.TypeSpec) 76 spec := decl.Specs[0].(*ast.TypeSpec)
77 typ := doc.lookupTypeDoc(spec.Name.Name) 77 typ := doc.lookupTypeDoc(spec.Name.Name)
78 » ÿ// typ should always be != nil since declared typesÿ 78 » // typ should always be != nil since declared types
79 » ÿ// are always named - be conservative and checkÿ 79 » // are always named - be conservative and check
80 if typ != nil { 80 if typ != nil {
81 » » ÿ// a type should be added at most once, so typ.declÿ 81 » » // a type should be added at most once, so typ.decl
82 » » ÿ// should be nil - if it isn't, simply overwrite itÿ 82 » » // should be nil - if it isn't, simply overwrite it
83 typ.decl = decl 83 typ.decl = decl
84 } 84 }
85 } 85 }
86 86
87 87
88 func (doc *docReader) lookupTypeDoc(name string) *typeDoc { 88 func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
89 if name == "" { 89 if name == "" {
90 » » return nil ÿ// no type docs for anonymous typesÿ 90 » » return nil // no type docs for anonymous types
91 } 91 }
92 if tdoc, found := doc.types[name]; found { 92 if tdoc, found := doc.types[name]; found {
93 return tdoc 93 return tdoc
94 } 94 }
95 » ÿ// type wasn't found - add one without declarationÿ 95 » // type wasn't found - add one without declaration
96 tdoc := &typeDoc{nil, nil, make(map[string]*ast.FuncDecl), make(map[stri ng]*ast.FuncDecl)} 96 tdoc := &typeDoc{nil, nil, make(map[string]*ast.FuncDecl), make(map[stri ng]*ast.FuncDecl)}
97 doc.types[name] = tdoc 97 doc.types[name] = tdoc
98 return tdoc 98 return tdoc
99 } 99 }
100 100
101 101
102 func baseTypeName(typ ast.Expr) string { 102 func baseTypeName(typ ast.Expr) string {
103 switch t := typ.(type) { 103 switch t := typ.(type) {
104 case *ast.Ident: 104 case *ast.Ident:
105 » » ÿ// if the type is not exported, the effect toÿ 105 » » // if the type is not exported, the effect to
106 » » ÿ// a client is as if there were no type nameÿ 106 » » // a client is as if there were no type name
107 if t.IsExported() { 107 if t.IsExported() {
108 return t.Name 108 return t.Name
109 } 109 }
110 case *ast.StarExpr: 110 case *ast.StarExpr:
111 return baseTypeName(t.X) 111 return baseTypeName(t.X)
112 } 112 }
113 return "" 113 return ""
114 } 114 }
115 115
116 116
117 func (doc *docReader) addValue(decl *ast.GenDecl) { 117 func (doc *docReader) addValue(decl *ast.GenDecl) {
118 » ÿ// determine if decl should be associated with a typeÿ 118 » // determine if decl should be associated with a type
119 » ÿ// Heuristic: For each typed entry, determine the type name, if any.ÿ 119 » // Heuristic: For each typed entry, determine the type name, if any.
120 » ÿ// If there is exactly one type name that is sufficientlyÿ 120 » // If there is exactly one type name that is sufficiently
121 » ÿ// frequent, associate the decl with the respective type.ÿ 121 » // frequent, associate the decl with the respective type.
122 domName := "" 122 domName := ""
123 domFreq := 0 123 domFreq := 0
124 prev := "" 124 prev := ""
125 for _, s := range decl.Specs { 125 for _, s := range decl.Specs {
126 if v, ok := s.(*ast.ValueSpec); ok { 126 if v, ok := s.(*ast.ValueSpec); ok {
127 name := "" 127 name := ""
128 switch { 128 switch {
129 case v.Type != nil: 129 case v.Type != nil:
130 » » » » ÿ// a type is present; determine its nameÿ 130 » » » » // a type is present; determine its name
131 name = baseTypeName(v.Type) 131 name = baseTypeName(v.Type)
132 case decl.Tok == token.CONST: 132 case decl.Tok == token.CONST:
133 » » » » ÿ// no type is present but we have a constant de claration;ÿ 133 » » » » // no type is present but we have a constant dec laration;
134 » » » » ÿ// use the previous type name (w/o more type in formationÿ 134 » » » » // use the previous type name (w/o more type inf ormation
135 » » » » ÿ// we cannot handle the case of unnamed variabl es withÿ 135 » » » » // we cannot handle the case of unnamed variable s with
136 » » » » ÿ// initializer expressions except for some triv ial cases)ÿ 136 » » » » // initializer expressions except for some trivi al cases)
137 name = prev 137 name = prev
138 } 138 }
139 if name != "" { 139 if name != "" {
140 » » » » ÿ// entry has a named typeÿ 140 » » » » // entry has a named type
141 if domName != "" && domName != name { 141 if domName != "" && domName != name {
142 » » » » » ÿ// more than one type name - do not ass ociateÿ 142 » » » » » // more than one type name - do not asso ciate
143 » » » » » ÿ// with any typeÿ 143 » » » » » // with any type
144 domName = "" 144 domName = ""
145 break 145 break
146 } 146 }
147 domName = name 147 domName = name
148 domFreq++ 148 domFreq++
149 } 149 }
150 prev = name 150 prev = name
151 } 151 }
152 } 152 }
153 153
154 » ÿ// determine values listÿ 154 » // determine values list
155 const threshold = 0.75 155 const threshold = 0.75
156 values := &doc.values 156 values := &doc.values
157 if domName != "" && domFreq >= int(float64(len(decl.Specs))*threshold) { 157 if domName != "" && domFreq >= int(float64(len(decl.Specs))*threshold) {
158 » » ÿ// typed entries are sufficiently frequentÿ 158 » » // typed entries are sufficiently frequent
159 typ := doc.lookupTypeDoc(domName) 159 typ := doc.lookupTypeDoc(domName)
160 if typ != nil { 160 if typ != nil {
161 » » » values = &typ.values ÿ// associate with that typeÿ 161 » » » values = &typ.values // associate with that type
162 } 162 }
163 } 163 }
164 164
165 *values = append(*values, decl) 165 *values = append(*values, decl)
166 } 166 }
167 167
168 168
169 ÿ// Helper function to set the table entry for function f. Makes sure thatÿ 169 // Helper function to set the table entry for function f. Makes sure that
170 ÿ// at least one f with associated documentation is stored in table, if thereÿ 170 // at least one f with associated documentation is stored in table, if there
171 ÿ// are multiple f's with the same name.ÿ 171 // are multiple f's with the same name.
172 func setFunc(table map[string]*ast.FuncDecl, f *ast.FuncDecl) { 172 func setFunc(table map[string]*ast.FuncDecl, f *ast.FuncDecl) {
173 name := f.Name.Name 173 name := f.Name.Name
174 if g, exists := table[name]; exists && g.Doc != nil { 174 if g, exists := table[name]; exists && g.Doc != nil {
175 » » ÿ// a function with the same name has already been registered;ÿ 175 » » // a function with the same name has already been registered;
176 » » ÿ// since it has documentation, assume f is simply anotherÿ 176 » » // since it has documentation, assume f is simply another
177 » » ÿ// implementation and ignore itÿ 177 » » // implementation and ignore it
178 » » ÿ// TODO(gri) consider collecting all functions, or at leastÿ 178 » » // TODO(gri) consider collecting all functions, or at least
179 » » ÿ// all commentsÿ 179 » » // all comments
180 return 180 return
181 } 181 }
182 » ÿ// function doesn't exist or has no documentation; use fÿ 182 » // function doesn't exist or has no documentation; use f
183 table[name] = f 183 table[name] = f
184 } 184 }
185 185
186 186
187 func (doc *docReader) addFunc(fun *ast.FuncDecl) { 187 func (doc *docReader) addFunc(fun *ast.FuncDecl) {
188 name := fun.Name.Name 188 name := fun.Name.Name
189 189
190 » ÿ// determine if it should be associated with a typeÿ 190 » // determine if it should be associated with a type
191 if fun.Recv != nil { 191 if fun.Recv != nil {
192 » » ÿ// methodÿ 192 » » // method
193 typ := doc.lookupTypeDoc(baseTypeName(fun.Recv.List[0].Type)) 193 typ := doc.lookupTypeDoc(baseTypeName(fun.Recv.List[0].Type))
194 if typ != nil { 194 if typ != nil {
195 » » » ÿ// exported receiver typeÿ 195 » » » // exported receiver type
196 setFunc(typ.methods, fun) 196 setFunc(typ.methods, fun)
197 } 197 }
198 » » ÿ// otherwise don't show the methodÿ 198 » » // otherwise don't show the method
199 » » ÿ// TODO(gri): There may be exported methods of non-exported typ esÿ 199 » » // TODO(gri): There may be exported methods of non-exported type s
200 » » ÿ// that can be called because of exported values (consts, vars, orÿ 200 » » // that can be called because of exported values (consts, vars, or
201 » » ÿ// function results) of that type. Could determine if that is t heÿ 201 » » // function results) of that type. Could determine if that is th e
202 » » ÿ// case and then show those methods in an appropriate section.ÿ 202 » » // case and then show those methods in an appropriate section.
203 return 203 return
204 } 204 }
205 205
206 » ÿ// perhaps a factory functionÿ 206 » // perhaps a factory function
207 » ÿ// determine result type, if anyÿ 207 » // determine result type, if any
208 if fun.Type.Results.NumFields() >= 1 { 208 if fun.Type.Results.NumFields() >= 1 {
209 res := fun.Type.Results.List[0] 209 res := fun.Type.Results.List[0]
210 if len(res.Names) <= 1 { 210 if len(res.Names) <= 1 {
211 » » » ÿ// exactly one (named or anonymous) result associatedÿ 211 » » » // exactly one (named or anonymous) result associated
212 » » » ÿ// with the first type in result signature (there mayÿ 212 » » » // with the first type in result signature (there may
213 » » » ÿ// be more than one result)ÿ 213 » » » // be more than one result)
214 tname := baseTypeName(res.Type) 214 tname := baseTypeName(res.Type)
215 typ := doc.lookupTypeDoc(tname) 215 typ := doc.lookupTypeDoc(tname)
216 if typ != nil { 216 if typ != nil {
217 » » » » ÿ// named and exported result typeÿ 217 » » » » // named and exported result type
218 218
219 » » » » ÿ// Work-around for failure of heuristic: In pac kage osÿ 219 » » » » // Work-around for failure of heuristic: In pack age os
220 » » » » ÿ// too many functions are considered factory fu nctionsÿ 220 » » » » // too many functions are considered factory fun ctions
221 » » » » ÿ// for the Error type. Eliminate manually for n ow asÿ 221 » » » » // for the Error type. Eliminate manually for no w as
222 » » » » ÿ// this appears to be the only important case i n theÿ 222 » » » » // this appears to be the only important case in the
223 » » » » ÿ// current library where the heuristic fails.ÿ 223 » » » » // current library where the heuristic fails.
224 if doc.pkgName == "os" && tname == "Error" && 224 if doc.pkgName == "os" && tname == "Error" &&
225 name != "NewError" && name != "NewSyscal lError" { 225 name != "NewError" && name != "NewSyscal lError" {
226 » » » » » ÿ// not a factory function for os.Errorÿ 226 » » » » » // not a factory function for os.Error
227 » » » » » setFunc(doc.funcs, fun) ÿ// treat as ord inary functionÿ 227 » » » » » setFunc(doc.funcs, fun) // treat as ordi nary function
228 return 228 return
229 } 229 }
230 230
231 setFunc(typ.factories, fun) 231 setFunc(typ.factories, fun)
232 return 232 return
233 } 233 }
234 } 234 }
235 } 235 }
236 236
237 » ÿ// ordinary functionÿ 237 » // ordinary function
238 setFunc(doc.funcs, fun) 238 setFunc(doc.funcs, fun)
239 } 239 }
240 240
241 241
242 func (doc *docReader) addDecl(decl ast.Decl) { 242 func (doc *docReader) addDecl(decl ast.Decl) {
243 switch d := decl.(type) { 243 switch d := decl.(type) {
244 case *ast.GenDecl: 244 case *ast.GenDecl:
245 if len(d.Specs) > 0 { 245 if len(d.Specs) > 0 {
246 switch d.Tok { 246 switch d.Tok {
247 case token.CONST, token.VAR: 247 case token.CONST, token.VAR:
248 » » » » ÿ// constants and variables are always handled a s a groupÿ 248 » » » » // constants and variables are always handled as a group
249 doc.addValue(d) 249 doc.addValue(d)
250 case token.TYPE: 250 case token.TYPE:
251 » » » » ÿ// types are handled individuallyÿ 251 » » » » // types are handled individually
252 for _, spec := range d.Specs { 252 for _, spec := range d.Specs {
253 » » » » » ÿ// make a (fake) GenDecl node for this TypeSpecÿ 253 » » » » » // make a (fake) GenDecl node for this T ypeSpec
254 » » » » » ÿ// (we need to do this here - as oppose d to justÿ 254 » » » » » // (we need to do this here - as opposed to just
255 » » » » » ÿ// for printing - so we don't lose the GenDeclÿ 255 » » » » » // for printing - so we don't lose the G enDecl
256 » » » » » ÿ// documentation)ÿ 256 » » » » » // documentation)
257 » » » » » ÿ//ÿ 257 » » » » » //
258 » » » » » ÿ// TODO(gri): Consider just collecting the TypeSpecÿ 258 » » » » » // TODO(gri): Consider just collecting t he TypeSpec
259 » » » » » ÿ// node (and copy in the GenDecl.doc if there is noÿ 259 » » » » » // node (and copy in the GenDecl.doc if there is no
260 » » » » » ÿ// doc in the TypeSpec - this is curren tly done inÿ 260 » » » » » // doc in the TypeSpec - this is current ly done in
261 » » » » » ÿ// makeTypeDocs below). Simpler data st ructures, butÿ 261 » » » » » // makeTypeDocs below). Simpler data str uctures, but
262 » » » » » ÿ// would lose GenDecl documentation if the TypeSpecÿ 262 » » » » » // would lose GenDecl documentation if t he TypeSpec
263 » » » » » ÿ// has documentation as well.ÿ 263 » » » » » // has documentation as well.
264 doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos, []ast.Spec{spec}, token.NoPos}) 264 doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos, []ast.Spec{spec}, token.NoPos})
265 » » » » » ÿ// A new GenDecl node is created, no ne ed to nil out d.Doc.ÿ 265 » » » » » // A new GenDecl node is created, no nee d to nil out d.Doc.
266 } 266 }
267 } 267 }
268 } 268 }
269 case *ast.FuncDecl: 269 case *ast.FuncDecl:
270 doc.addFunc(d) 270 doc.addFunc(d)
271 } 271 }
272 } 272 }
273 273
274 274
275 func copyCommentList(list []*ast.Comment) []*ast.Comment { 275 func copyCommentList(list []*ast.Comment) []*ast.Comment {
276 return append([]*ast.Comment(nil), list...) 276 return append([]*ast.Comment(nil), list...)
277 } 277 }
278 278
279 var ( 279 var (
280 » bug_markers = regexp.MustCompile("^/[/*][ \t]*BUG\\(.*\\):[ \t]*") ÿ// B UG(uid):ÿ 280 » bug_markers = regexp.MustCompile("^/[/*][ \t]*BUG\\(.*\\):[ \t]*") // BU G(uid):
281 » bug_content = regexp.MustCompile("[^ \n\r\t]+") ÿ// a t least one non-whitespace charÿ 281 » bug_content = regexp.MustCompile("[^ \n\r\t]+") // at least one non-whitespace char
282 ) 282 )
283 283
284 284
285 ÿ// addFile adds the AST for a source file to the docReader.ÿ 285 // addFile adds the AST for a source file to the docReader.
286 ÿ// Adding the same AST multiple times is a no-op.ÿ 286 // Adding the same AST multiple times is a no-op.
287 ÿ//ÿ 287 //
288 func (doc *docReader) addFile(src *ast.File) { 288 func (doc *docReader) addFile(src *ast.File) {
289 » ÿ// add package documentationÿ 289 » // add package documentation
290 if src.Doc != nil { 290 if src.Doc != nil {
291 doc.addDoc(src.Doc) 291 doc.addDoc(src.Doc)
292 » » src.Doc = nil ÿ// doc consumed - remove from ast.File nodeÿ 292 » » src.Doc = nil // doc consumed - remove from ast.File node
293 » } 293 » }
294 294
295 » ÿ// add all declarationsÿ 295 » // add all declarations
296 for _, decl := range src.Decls { 296 for _, decl := range src.Decls {
297 doc.addDecl(decl) 297 doc.addDecl(decl)
298 } 298 }
299 299
300 » ÿ// collect BUG(...) commentsÿ 300 » // collect BUG(...) comments
301 for _, c := range src.Comments { 301 for _, c := range src.Comments {
302 text := c.List[0].Text 302 text := c.List[0].Text
303 if m := bug_markers.FindStringIndex(text); m != nil { 303 if m := bug_markers.FindStringIndex(text); m != nil {
304 » » » ÿ// found a BUG comment; maybe emptyÿ 304 » » » // found a BUG comment; maybe empty
305 if btxt := text[m[1]:]; bug_content.MatchString(btxt) { 305 if btxt := text[m[1]:]; bug_content.MatchString(btxt) {
306 » » » » ÿ// non-empty BUG comment; collect comment witho ut BUG prefixÿ 306 » » » » // non-empty BUG comment; collect comment withou t BUG prefix
307 list := copyCommentList(c.List) 307 list := copyCommentList(c.List)
308 list[0].Text = text[m[1]:] 308 list[0].Text = text[m[1]:]
309 doc.bugs = append(doc.bugs, &ast.CommentGroup{li st}) 309 doc.bugs = append(doc.bugs, &ast.CommentGroup{li st})
310 } 310 }
311 } 311 }
312 } 312 }
313 » src.Comments = nil ÿ// consumed unassociated comments - remove from ast. File nodeÿ 313 » src.Comments = nil // consumed unassociated comments - remove from ast.F ile node
314 } 314 }
315 315
316 316
317 func NewFileDoc(file *ast.File) *PackageDoc { 317 func NewFileDoc(file *ast.File) *PackageDoc {
318 var r docReader 318 var r docReader
319 r.init(file.Name.Name) 319 r.init(file.Name.Name)
320 r.addFile(file) 320 r.addFile(file)
321 return r.newDoc("", nil) 321 return r.newDoc("", nil)
322 } 322 }
323 323
324 324
325 func NewPackageDoc(pkg *ast.Package, importpath string) *PackageDoc { 325 func NewPackageDoc(pkg *ast.Package, importpath string) *PackageDoc {
326 var r docReader 326 var r docReader
327 r.init(pkg.Name) 327 r.init(pkg.Name)
328 filenames := make([]string, len(pkg.Files)) 328 filenames := make([]string, len(pkg.Files))
329 i := 0 329 i := 0
330 for filename, f := range pkg.Files { 330 for filename, f := range pkg.Files {
331 r.addFile(f) 331 r.addFile(f)
332 filenames[i] = filename 332 filenames[i] = filename
333 i++ 333 i++
334 } 334 }
335 return r.newDoc(importpath, filenames) 335 return r.newDoc(importpath, filenames)
336 } 336 }
337 337
338 338
339 ÿ// ---------------------------------------------------------------------------- ÿ 339 // ----------------------------------------------------------------------------
340 ÿ// Conversion to external representationÿ 340 // Conversion to external representation
341 341
342 ÿ// ValueDoc is the documentation for a group of declaredÿ 342 // ValueDoc is the documentation for a group of declared
343 ÿ// values, either vars or consts.ÿ 343 // values, either vars or consts.
344 ÿ//ÿ 344 //
345 type ValueDoc struct { 345 type ValueDoc struct {
346 Doc string 346 Doc string
347 Decl *ast.GenDecl 347 Decl *ast.GenDecl
348 order int 348 order int
349 } 349 }
350 350
351 type sortValueDoc []*ValueDoc 351 type sortValueDoc []*ValueDoc
352 352
353 func (p sortValueDoc) Len() int { return len(p) } 353 func (p sortValueDoc) Len() int { return len(p) }
354 func (p sortValueDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 354 func (p sortValueDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
355 355
356 356
357 func declName(d *ast.GenDecl) string { 357 func declName(d *ast.GenDecl) string {
358 if len(d.Specs) != 1 { 358 if len(d.Specs) != 1 {
359 return "" 359 return ""
360 } 360 }
361 361
362 switch v := d.Specs[0].(type) { 362 switch v := d.Specs[0].(type) {
363 case *ast.ValueSpec: 363 case *ast.ValueSpec:
364 return v.Names[0].Name 364 return v.Names[0].Name
365 case *ast.TypeSpec: 365 case *ast.TypeSpec:
366 return v.Name.Name 366 return v.Name.Name
367 } 367 }
368 368
369 return "" 369 return ""
370 } 370 }
371 371
372 372
373 func (p sortValueDoc) Less(i, j int) bool { 373 func (p sortValueDoc) Less(i, j int) bool {
374 » ÿ// sort by nameÿ 374 » // sort by name
375 » ÿ// pull blocks (name = "") up to topÿ 375 » // pull blocks (name = "") up to top
376 » ÿ// in original orderÿ 376 » // in original order
377 if ni, nj := declName(p[i].Decl), declName(p[j].Decl); ni != nj { 377 if ni, nj := declName(p[i].Decl), declName(p[j].Decl); ni != nj {
378 return ni < nj 378 return ni < nj
379 } 379 }
380 return p[i].order < p[j].order 380 return p[i].order < p[j].order
381 } 381 }
382 382
383 383
384 func makeValueDocs(list []*ast.GenDecl, tok token.Token) []*ValueDoc { 384 func makeValueDocs(list []*ast.GenDecl, tok token.Token) []*ValueDoc {
385 » d := make([]*ValueDoc, len(list)) ÿ// big enough in any caseÿ 385 » d := make([]*ValueDoc, len(list)) // big enough in any case
386 n := 0 386 n := 0
387 for i, decl := range list { 387 for i, decl := range list {
388 if decl.Tok == tok { 388 if decl.Tok == tok {
389 d[n] = &ValueDoc{CommentText(decl.Doc), decl, i} 389 d[n] = &ValueDoc{CommentText(decl.Doc), decl, i}
390 n++ 390 n++
391 » » » decl.Doc = nil ÿ// doc consumed - removed from ASTÿ 391 » » » decl.Doc = nil // doc consumed - removed from AST
392 } 392 }
393 } 393 }
394 d = d[0:n] 394 d = d[0:n]
395 sort.Sort(sortValueDoc(d)) 395 sort.Sort(sortValueDoc(d))
396 return d 396 return d
397 } 397 }
398 398
399 399
400 ÿ// FuncDoc is the documentation for a func declaration,ÿ 400 // FuncDoc is the documentation for a func declaration,
401 ÿ// either a top-level function or a method function.ÿ 401 // either a top-level function or a method function.
402 ÿ//ÿ 402 //
403 type FuncDoc struct { 403 type FuncDoc struct {
404 Doc string 404 Doc string
405 » Recv ast.Expr ÿ// TODO(rsc): Would like string hereÿ 405 » Recv ast.Expr // TODO(rsc): Would like string here
406 Name string 406 Name string
407 Decl *ast.FuncDecl 407 Decl *ast.FuncDecl
408 } 408 }
409 409
410 type sortFuncDoc []*FuncDoc 410 type sortFuncDoc []*FuncDoc
411 411
412 func (p sortFuncDoc) Len() int { return len(p) } 412 func (p sortFuncDoc) Len() int { return len(p) }
413 func (p sortFuncDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 413 func (p sortFuncDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
414 func (p sortFuncDoc) Less(i, j int) bool { return p[i].Name < p[j].Name } 414 func (p sortFuncDoc) Less(i, j int) bool { return p[i].Name < p[j].Name }
415 415
416 416
417 func makeFuncDocs(m map[string]*ast.FuncDecl) []*FuncDoc { 417 func makeFuncDocs(m map[string]*ast.FuncDecl) []*FuncDoc {
418 d := make([]*FuncDoc, len(m)) 418 d := make([]*FuncDoc, len(m))
419 i := 0 419 i := 0
420 for _, f := range m { 420 for _, f := range m {
421 doc := new(FuncDoc) 421 doc := new(FuncDoc)
422 doc.Doc = CommentText(f.Doc) 422 doc.Doc = CommentText(f.Doc)
423 » » f.Doc = nil ÿ// doc consumed - remove from ast.FuncDecl nodeÿ 423 » » f.Doc = nil // doc consumed - remove from ast.FuncDecl node
424 if f.Recv != nil { 424 if f.Recv != nil {
425 doc.Recv = f.Recv.List[0].Type 425 doc.Recv = f.Recv.List[0].Type
426 } 426 }
427 doc.Name = f.Name.Name 427 doc.Name = f.Name.Name
428 doc.Decl = f 428 doc.Decl = f
429 d[i] = doc 429 d[i] = doc
430 i++ 430 i++
431 } 431 }
432 sort.Sort(sortFuncDoc(d)) 432 sort.Sort(sortFuncDoc(d))
433 return d 433 return d
434 } 434 }
435 435
436 436
437 ÿ// TypeDoc is the documentation for a declared type.ÿ 437 // TypeDoc is the documentation for a declared type.
438 ÿ// Consts and Vars are sorted lists of constants and variables of (mostly) that type.ÿ 438 // Consts and Vars are sorted lists of constants and variables of (mostly) that type.
439 ÿ// Factories is a sorted list of factory functions that return that type.ÿ 439 // Factories is a sorted list of factory functions that return that type.
440 ÿ// Methods is a sorted list of method functions on that type.ÿ 440 // Methods is a sorted list of method functions on that type.
441 type TypeDoc struct { 441 type TypeDoc struct {
442 Doc string 442 Doc string
443 Type *ast.TypeSpec 443 Type *ast.TypeSpec
444 Consts []*ValueDoc 444 Consts []*ValueDoc
445 Vars []*ValueDoc 445 Vars []*ValueDoc
446 Factories []*FuncDoc 446 Factories []*FuncDoc
447 Methods []*FuncDoc 447 Methods []*FuncDoc
448 Decl *ast.GenDecl 448 Decl *ast.GenDecl
449 order int 449 order int
450 } 450 }
451 451
452 type sortTypeDoc []*TypeDoc 452 type sortTypeDoc []*TypeDoc
453 453
454 func (p sortTypeDoc) Len() int { return len(p) } 454 func (p sortTypeDoc) Len() int { return len(p) }
455 func (p sortTypeDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 455 func (p sortTypeDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
456 func (p sortTypeDoc) Less(i, j int) bool { 456 func (p sortTypeDoc) Less(i, j int) bool {
457 » ÿ// sort by nameÿ 457 » // sort by name
458 » ÿ// pull blocks (name = "") up to topÿ 458 » // pull blocks (name = "") up to top
459 » ÿ// in original orderÿ 459 » // in original order
460 if ni, nj := p[i].Type.Name.Name, p[j].Type.Name.Name; ni != nj { 460 if ni, nj := p[i].Type.Name.Name, p[j].Type.Name.Name; ni != nj {
461 return ni < nj 461 return ni < nj
462 } 462 }
463 return p[i].order < p[j].order 463 return p[i].order < p[j].order
464 } 464 }
465 465
466 466
467 ÿ// NOTE(rsc): This would appear not to be correct for type ( )ÿ 467 // NOTE(rsc): This would appear not to be correct for type ( )
468 ÿ// blocks, but the doc extractor above has split them intoÿ 468 // blocks, but the doc extractor above has split them into
469 ÿ// individual declarations.ÿ 469 // individual declarations.
470 func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc { 470 func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {
471 d := make([]*TypeDoc, len(m)) 471 d := make([]*TypeDoc, len(m))
472 i := 0 472 i := 0
473 for _, old := range m { 473 for _, old := range m {
474 » » ÿ// all typeDocs should have a declaration associated withÿ 474 » » // all typeDocs should have a declaration associated with
475 » » ÿ// them after processing an entire package - be conservativeÿ 475 » » // them after processing an entire package - be conservative
476 » » ÿ// and checkÿ 476 » » // and check
477 if decl := old.decl; decl != nil { 477 if decl := old.decl; decl != nil {
478 typespec := decl.Specs[0].(*ast.TypeSpec) 478 typespec := decl.Specs[0].(*ast.TypeSpec)
479 t := new(TypeDoc) 479 t := new(TypeDoc)
480 doc := typespec.Doc 480 doc := typespec.Doc
481 » » » typespec.Doc = nil ÿ// doc consumed - remove from ast.Ty peSpec nodeÿ 481 » » » typespec.Doc = nil // doc consumed - remove from ast.Typ eSpec node
482 if doc == nil { 482 if doc == nil {
483 » » » » ÿ// no doc associated with the spec, use the dec laration doc, if anyÿ 483 » » » » // no doc associated with the spec, use the decl aration doc, if any
484 doc = decl.Doc 484 doc = decl.Doc
485 } 485 }
486 » » » decl.Doc = nil ÿ// doc consumed - remove from ast.Decl n odeÿ 486 » » » decl.Doc = nil // doc consumed - remove from ast.Decl no de
487 t.Doc = CommentText(doc) 487 t.Doc = CommentText(doc)
488 t.Type = typespec 488 t.Type = typespec
489 t.Consts = makeValueDocs(old.values, token.CONST) 489 t.Consts = makeValueDocs(old.values, token.CONST)
490 t.Vars = makeValueDocs(old.values, token.VAR) 490 t.Vars = makeValueDocs(old.values, token.VAR)
491 t.Factories = makeFuncDocs(old.factories) 491 t.Factories = makeFuncDocs(old.factories)
492 t.Methods = makeFuncDocs(old.methods) 492 t.Methods = makeFuncDocs(old.methods)
493 t.Decl = old.decl 493 t.Decl = old.decl
494 t.order = i 494 t.order = i
495 d[i] = t 495 d[i] = t
496 i++ 496 i++
497 } else { 497 } else {
498 » » » ÿ// no corresponding type declaration found - move any a ssociatedÿ 498 » » » // no corresponding type declaration found - move any as sociated
499 » » » ÿ// values, factory functions, and methods back to the t op-levelÿ 499 » » » // values, factory functions, and methods back to the to p-level
500 » » » ÿ// so that they are not lost (this should only happen i f a packageÿ 500 » » » // so that they are not lost (this should only happen if a package
501 » » » ÿ// file containing the explicit type declaration is mis sing or ifÿ 501 » » » // file containing the explicit type declaration is miss ing or if
502 » » » ÿ// an unqualified type name was used after a "." import )ÿ 502 » » » // an unqualified type name was used after a "." import)
503 » » » ÿ// 1) move valuesÿ 503 » » » // 1) move values
504 doc.values = append(doc.values, old.values...) 504 doc.values = append(doc.values, old.values...)
505 » » » ÿ// 2) move factory functionsÿ 505 » » » // 2) move factory functions
506 for name, f := range old.factories { 506 for name, f := range old.factories {
507 doc.funcs[name] = f 507 doc.funcs[name] = f
508 } 508 }
509 » » » ÿ// 3) move methodsÿ 509 » » » // 3) move methods
510 for name, f := range old.methods { 510 for name, f := range old.methods {
511 » » » » ÿ// don't overwrite functions with the same name ÿ 511 » » » » // don't overwrite functions with the same name
512 if _, found := doc.funcs[name]; !found { 512 if _, found := doc.funcs[name]; !found {
513 doc.funcs[name] = f 513 doc.funcs[name] = f
514 } 514 }
515 } 515 }
516 } 516 }
517 } 517 }
518 » d = d[0:i] ÿ// some types may have been ignoredÿ 518 » d = d[0:i] // some types may have been ignored
519 sort.Sort(sortTypeDoc(d)) 519 sort.Sort(sortTypeDoc(d))
520 return d 520 return d
521 } 521 }
522 522
523 523
524 func makeBugDocs(list []*ast.CommentGroup) []string { 524 func makeBugDocs(list []*ast.CommentGroup) []string {
525 d := make([]string, len(list)) 525 d := make([]string, len(list))
526 for i, g := range list { 526 for i, g := range list {
527 d[i] = CommentText(g) 527 d[i] = CommentText(g)
528 } 528 }
529 return d 529 return d
530 } 530 }
531 531
532 532
533 ÿ// PackageDoc is the documentation for an entire package.ÿ 533 // PackageDoc is the documentation for an entire package.
534 ÿ//ÿ 534 //
535 type PackageDoc struct { 535 type PackageDoc struct {
536 PackageName string 536 PackageName string
537 ImportPath string 537 ImportPath string
538 Filenames []string 538 Filenames []string
539 Doc string 539 Doc string
540 Consts []*ValueDoc 540 Consts []*ValueDoc
541 Types []*TypeDoc 541 Types []*TypeDoc
542 Vars []*ValueDoc 542 Vars []*ValueDoc
543 Funcs []*FuncDoc 543 Funcs []*FuncDoc
544 Bugs []string 544 Bugs []string
545 } 545 }
546 546
547 547
548 ÿ// newDoc returns the accumulated documentation for the package.ÿ 548 // newDoc returns the accumulated documentation for the package.
549 ÿ//ÿ 549 //
550 func (doc *docReader) newDoc(importpath string, filenames []string) *PackageDoc { 550 func (doc *docReader) newDoc(importpath string, filenames []string) *PackageDoc {
551 p := new(PackageDoc) 551 p := new(PackageDoc)
552 p.PackageName = doc.pkgName 552 p.PackageName = doc.pkgName
553 p.ImportPath = importpath 553 p.ImportPath = importpath
554 sort.SortStrings(filenames) 554 sort.SortStrings(filenames)
555 p.Filenames = filenames 555 p.Filenames = filenames
556 p.Doc = CommentText(doc.doc) 556 p.Doc = CommentText(doc.doc)
557 » ÿ// makeTypeDocs may extend the list of doc.values andÿ 557 » // makeTypeDocs may extend the list of doc.values and
558 » ÿ// doc.funcs and thus must be called before any otherÿ 558 » // doc.funcs and thus must be called before any other
559 » ÿ// function consuming those listsÿ 559 » // function consuming those lists
560 p.Types = doc.makeTypeDocs(doc.types) 560 p.Types = doc.makeTypeDocs(doc.types)
561 p.Consts = makeValueDocs(doc.values, token.CONST) 561 p.Consts = makeValueDocs(doc.values, token.CONST)
562 p.Vars = makeValueDocs(doc.values, token.VAR) 562 p.Vars = makeValueDocs(doc.values, token.VAR)
563 p.Funcs = makeFuncDocs(doc.funcs) 563 p.Funcs = makeFuncDocs(doc.funcs)
564 p.Bugs = makeBugDocs(doc.bugs) 564 p.Bugs = makeBugDocs(doc.bugs)
565 return p 565 return p
566 } 566 }
567 567
568 568
569 ÿ// ---------------------------------------------------------------------------- ÿ 569 // ----------------------------------------------------------------------------
570 ÿ// Filtering by nameÿ 570 // Filtering by name
571 571
572 type Filter func(string) bool 572 type Filter func(string) bool
573 573
574 574
575 func matchDecl(d *ast.GenDecl, f Filter) bool { 575 func matchDecl(d *ast.GenDecl, f Filter) bool {
576 for _, d := range d.Specs { 576 for _, d := range d.Specs {
577 switch v := d.(type) { 577 switch v := d.(type) {
578 case *ast.ValueSpec: 578 case *ast.ValueSpec:
579 for _, name := range v.Names { 579 for _, name := range v.Names {
580 if f(name.Name) { 580 if f(name.Name) {
(...skipping 30 matching lines...) Expand all
611 w++ 611 w++
612 } 612 }
613 } 613 }
614 return a[0:w] 614 return a[0:w]
615 } 615 }
616 616
617 617
618 func filterTypeDocs(a []*TypeDoc, f Filter) []*TypeDoc { 618 func filterTypeDocs(a []*TypeDoc, f Filter) []*TypeDoc {
619 w := 0 619 w := 0
620 for _, td := range a { 620 for _, td := range a {
621 » » n := 0 ÿ// number of matchesÿ 621 » » n := 0 // number of matches
622 if matchDecl(td.Decl, f) { 622 if matchDecl(td.Decl, f) {
623 n = 1 623 n = 1
624 } else { 624 } else {
625 » » » ÿ// type name doesn't match, but we may have matching co nsts, vars, factories or methodsÿ 625 » » » // type name doesn't match, but we may have matching con sts, vars, factories or methods
626 td.Consts = filterValueDocs(td.Consts, f) 626 td.Consts = filterValueDocs(td.Consts, f)
627 td.Vars = filterValueDocs(td.Vars, f) 627 td.Vars = filterValueDocs(td.Vars, f)
628 td.Factories = filterFuncDocs(td.Factories, f) 628 td.Factories = filterFuncDocs(td.Factories, f)
629 td.Methods = filterFuncDocs(td.Methods, f) 629 td.Methods = filterFuncDocs(td.Methods, f)
630 n += len(td.Consts) + len(td.Vars) + len(td.Factories) + len(td.Methods) 630 n += len(td.Consts) + len(td.Vars) + len(td.Factories) + len(td.Methods)
631 } 631 }
632 if n > 0 { 632 if n > 0 {
633 a[w] = td 633 a[w] = td
634 w++ 634 w++
635 } 635 }
636 } 636 }
637 return a[0:w] 637 return a[0:w]
638 } 638 }
639 639
640 640
641 ÿ// Filter eliminates documentation for names that don't pass through the filter f.ÿ 641 // Filter eliminates documentation for names that don't pass through the filter f.
642 ÿ// TODO: Recognize "Type.Method" as a name.ÿ 642 // TODO: Recognize "Type.Method" as a name.
643 ÿ//ÿ 643 //
644 func (p *PackageDoc) Filter(f Filter) { 644 func (p *PackageDoc) Filter(f Filter) {
645 p.Consts = filterValueDocs(p.Consts, f) 645 p.Consts = filterValueDocs(p.Consts, f)
646 p.Vars = filterValueDocs(p.Vars, f) 646 p.Vars = filterValueDocs(p.Vars, f)
647 p.Types = filterTypeDocs(p.Types, f) 647 p.Types = filterTypeDocs(p.Types, f)
648 p.Funcs = filterFuncDocs(p.Funcs, f) 648 p.Funcs = filterFuncDocs(p.Funcs, f)
649 » p.Doc = "" ÿ// don't show top-level package docÿ 649 » p.Doc = "" // don't show top-level package doc
650 } 650 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b