]>
Commit | Line | Data |
---|---|---|
7a938933 ILT |
1 | // Copyright 2009 The Go Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style | |
3 | // license that can be found in the LICENSE file. | |
4 | ||
9ff56c95 | 5 | // Package doc extracts source code documentation from a Go AST. |
7a938933 ILT |
6 | package doc |
7 | ||
8 | import ( | |
5a8ea165 | 9 | "fmt" |
7a938933 | 10 | "go/ast" |
9af4cb95 | 11 | "go/token" |
5a8ea165 | 12 | "strings" |
7a938933 ILT |
13 | ) |
14 | ||
df1304ee ILT |
15 | // Package is the documentation for an entire package. |
16 | type Package struct { | |
17 | Doc string | |
18 | Name string | |
19 | ImportPath string | |
af92e385 | 20 | Imports []string |
df1304ee | 21 | Filenames []string |
be47d6ec | 22 | Notes map[string][]*Note |
af146490 ILT |
23 | |
24 | // Deprecated: For backward compatibility Bugs is still populated, | |
be47d6ec ILT |
25 | // but all new code should use Notes instead. |
26 | Bugs []string | |
9af4cb95 ILT |
27 | |
28 | // declarations | |
29 | Consts []*Value | |
30 | Types []*Type | |
31 | Vars []*Value | |
32 | Funcs []*Func | |
5a8ea165 ILT |
33 | |
34 | // Examples is a sorted list of examples associated with | |
35 | // the package. Examples are extracted from _test.go files | |
36 | // provided to NewFromFiles. | |
37 | Examples []*Example | |
df1304ee ILT |
38 | } |
39 | ||
40 | // Value is the documentation for a (possibly grouped) var or const declaration. | |
41 | type Value struct { | |
7a938933 | 42 | Doc string |
df1304ee | 43 | Names []string // var or const names in declaration order |
7a938933 | 44 | Decl *ast.GenDecl |
df1304ee | 45 | |
7a938933 ILT |
46 | order int |
47 | } | |
48 | ||
9af4cb95 | 49 | // Type is the documentation for a type declaration. |
df1304ee | 50 | type Type struct { |
9af4cb95 ILT |
51 | Doc string |
52 | Name string | |
53 | Decl *ast.GenDecl | |
7a938933 | 54 | |
9af4cb95 ILT |
55 | // associated declarations |
56 | Consts []*Value // sorted list of constants of (mostly) this type | |
57 | Vars []*Value // sorted list of variables of (mostly) this type | |
58 | Funcs []*Func // sorted list of functions returning this type | |
59 | Methods []*Func // sorted list of methods (including embedded ones) of this type | |
5a8ea165 ILT |
60 | |
61 | // Examples is a sorted list of examples associated with | |
62 | // this type. Examples are extracted from _test.go files | |
63 | // provided to NewFromFiles. | |
64 | Examples []*Example | |
7a938933 ILT |
65 | } |
66 | ||
df1304ee ILT |
67 | // Func is the documentation for a func declaration. |
68 | type Func struct { | |
7a938933 | 69 | Doc string |
7a938933 ILT |
70 | Name string |
71 | Decl *ast.FuncDecl | |
9af4cb95 ILT |
72 | |
73 | // methods | |
74 | // (for functions, these fields have the respective zero value) | |
75 | Recv string // actual receiver "T" or "*T" | |
76 | Orig string // original receiver "T" or "*T" | |
77 | Level int // embedding level; 0 means not embedded | |
5a8ea165 ILT |
78 | |
79 | // Examples is a sorted list of examples associated with this | |
80 | // function or method. Examples are extracted from _test.go files | |
81 | // provided to NewFromFiles. | |
82 | Examples []*Example | |
7a938933 ILT |
83 | } |
84 | ||
be47d6ec ILT |
85 | // A Note represents a marked comment starting with "MARKER(uid): note body". |
86 | // Any note with a marker of 2 or more upper case [A-Z] letters and a uid of | |
87 | // at least one character is recognized. The ":" following the uid is optional. | |
88 | // Notes are collected in the Package.Notes map indexed by the notes marker. | |
89 | type Note struct { | |
90 | Pos, End token.Pos // position range of the comment containing the marker | |
91 | UID string // uid found with the marker | |
92 | Body string // note body text | |
93 | } | |
94 | ||
5a8ea165 | 95 | // Mode values control the operation of New and NewFromFiles. |
df1304ee | 96 | type Mode int |
7a938933 | 97 | |
df1304ee | 98 | const ( |
4f4a855d ILT |
99 | // AllDecls says to extract documentation for all package-level |
100 | // declarations, not just exported ones. | |
df1304ee | 101 | AllDecls Mode = 1 << iota |
94252f4b | 102 | |
4f4a855d ILT |
103 | // AllMethods says to show all embedded methods, not just the ones of |
104 | // invisible (unexported) anonymous fields. | |
94252f4b | 105 | AllMethods |
4f4a855d ILT |
106 | |
107 | // PreserveAST says to leave the AST unmodified. Originally, pieces of | |
108 | // the AST such as function bodies were nil-ed out to save memory in | |
109 | // godoc, but not all programs want that behavior. | |
110 | PreserveAST | |
df1304ee | 111 | ) |
df4aa89a | 112 | |
9af4cb95 ILT |
113 | // New computes the package documentation for the given package AST. |
114 | // New takes ownership of the AST pkg and may edit or overwrite it. | |
5a8ea165 ILT |
115 | // To have the Examples fields populated, use NewFromFiles and include |
116 | // the package's _test.go files. | |
9af4cb95 ILT |
117 | // |
118 | func New(pkg *ast.Package, importPath string, mode Mode) *Package { | |
119 | var r reader | |
120 | r.readPackage(pkg, mode) | |
121 | r.computeMethodSets() | |
122 | r.cleanupTypes() | |
123 | return &Package{ | |
124 | Doc: r.doc, | |
125 | Name: pkg.Name, | |
126 | ImportPath: importPath, | |
127 | Imports: sortedKeys(r.imports), | |
128 | Filenames: r.filenames, | |
be47d6ec ILT |
129 | Notes: r.notes, |
130 | Bugs: noteBodies(r.notes["BUG"]), | |
9af4cb95 | 131 | Consts: sortedValues(r.values, token.CONST), |
94252f4b | 132 | Types: sortedTypes(r.types, mode&AllMethods != 0), |
9af4cb95 | 133 | Vars: sortedValues(r.values, token.VAR), |
94252f4b | 134 | Funcs: sortedFuncs(r.funcs, true), |
df4aa89a | 135 | } |
7a938933 | 136 | } |
5a8ea165 ILT |
137 | |
138 | // NewFromFiles computes documentation for a package. | |
139 | // | |
140 | // The package is specified by a list of *ast.Files and corresponding | |
141 | // file set, which must not be nil. NewFromFiles does not skip files | |
142 | // based on build constraints, so it is the caller's responsibility to | |
143 | // provide only the files that are matched by the build context. | |
144 | // The import path of the package is specified by importPath. | |
145 | // | |
146 | // Examples found in _test.go files are associated with the corresponding | |
147 | // type, function, method, or the package, based on their name. | |
148 | // If the example has a suffix in its name, it is set in the | |
149 | // Example.Suffix field. Examples with malformed names are skipped. | |
150 | // | |
151 | // Optionally, a single extra argument of type Mode can be provided to | |
152 | // control low-level aspects of the documentation extraction behavior. | |
153 | // | |
154 | // NewFromFiles takes ownership of the AST files and may edit them, | |
155 | // unless the PreserveAST Mode bit is on. | |
156 | // | |
157 | func NewFromFiles(fset *token.FileSet, files []*ast.File, importPath string, opts ...interface{}) (*Package, error) { | |
158 | // Check for invalid API usage. | |
159 | if fset == nil { | |
160 | panic(fmt.Errorf("doc.NewFromFiles: no token.FileSet provided (fset == nil)")) | |
161 | } | |
162 | var mode Mode | |
163 | switch len(opts) { // There can only be 0 or 1 options, so a simple switch works for now. | |
164 | case 0: | |
165 | // Nothing to do. | |
166 | case 1: | |
167 | m, ok := opts[0].(Mode) | |
168 | if !ok { | |
169 | panic(fmt.Errorf("doc.NewFromFiles: option argument type must be doc.Mode")) | |
170 | } | |
171 | mode = m | |
172 | default: | |
173 | panic(fmt.Errorf("doc.NewFromFiles: there must not be more than 1 option argument")) | |
174 | } | |
175 | ||
176 | // Collect .go and _test.go files. | |
177 | var ( | |
178 | goFiles = make(map[string]*ast.File) | |
179 | testGoFiles []*ast.File | |
180 | ) | |
181 | for i := range files { | |
182 | f := fset.File(files[i].Pos()) | |
183 | if f == nil { | |
184 | return nil, fmt.Errorf("file files[%d] is not found in the provided file set", i) | |
185 | } | |
186 | switch name := f.Name(); { | |
187 | case strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go"): | |
188 | goFiles[name] = files[i] | |
189 | case strings.HasSuffix(name, "_test.go"): | |
190 | testGoFiles = append(testGoFiles, files[i]) | |
191 | default: | |
192 | return nil, fmt.Errorf("file files[%d] filename %q does not have a .go extension", i, name) | |
193 | } | |
194 | } | |
195 | ||
196 | // TODO(dmitshur,gri): A relatively high level call to ast.NewPackage with a simpleImporter | |
197 | // ast.Importer implementation is made below. It might be possible to short-circuit and simplify. | |
198 | ||
199 | // Compute package documentation. | |
200 | pkg, _ := ast.NewPackage(fset, goFiles, simpleImporter, nil) // Ignore errors that can happen due to unresolved identifiers. | |
201 | p := New(pkg, importPath, mode) | |
202 | classifyExamples(p, Examples(testGoFiles...)) | |
203 | return p, nil | |
204 | } | |
205 | ||
206 | // simpleImporter returns a (dummy) package object named by the last path | |
207 | // component of the provided package path (as is the convention for packages). | |
208 | // This is sufficient to resolve package identifiers without doing an actual | |
209 | // import. It never returns an error. | |
210 | func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) { | |
211 | pkg := imports[path] | |
212 | if pkg == nil { | |
213 | // note that strings.LastIndex returns -1 if there is no "/" | |
214 | pkg = ast.NewObj(ast.Pkg, path[strings.LastIndex(path, "/")+1:]) | |
215 | pkg.Data = ast.NewScope(nil) // required by ast.NewPackage for dot-import | |
216 | imports[path] = pkg | |
217 | } | |
218 | return pkg, nil | |
219 | } |