1
2
3
4
5 package modindex
6
7 import (
8 "cmd/go/internal/base"
9 "cmd/go/internal/fsys"
10 "cmd/go/internal/str"
11 "encoding/json"
12 "errors"
13 "fmt"
14 "go/build"
15 "go/doc"
16 "go/scanner"
17 "go/token"
18 "io/fs"
19 "path/filepath"
20 "strings"
21 )
22
23
24
25
26 func moduleWalkErr(root string, path string, info fs.FileInfo, err error) error {
27 if err != nil {
28 return ErrNotIndexed
29 }
30
31 if info.IsDir() && path != root {
32 if fi, err := fsys.Stat(filepath.Join(path, "go.mod")); err == nil && !fi.IsDir() {
33 return filepath.SkipDir
34 }
35 }
36 if info.Mode()&fs.ModeSymlink != 0 {
37 if target, err := fsys.Stat(path); err == nil && target.IsDir() {
38
39
40
41
42
43 return ErrNotIndexed
44 }
45 }
46 return nil
47 }
48
49
50
51
52 func indexModule(modroot string) ([]byte, error) {
53 fsys.Trace("indexModule", modroot)
54 var packages []*rawPackage
55
56
57
58
59 root := str.WithFilePathSeparator(modroot)
60 err := fsys.Walk(root, func(path string, info fs.FileInfo, err error) error {
61 if err := moduleWalkErr(root, path, info, err); err != nil {
62 return err
63 }
64
65 if !info.IsDir() {
66 return nil
67 }
68 if !strings.HasPrefix(path, root) {
69 panic(fmt.Errorf("path %v in walk doesn't have modroot %v as prefix", path, modroot))
70 }
71 rel := path[len(root):]
72 packages = append(packages, importRaw(modroot, rel))
73 return nil
74 })
75 if err != nil {
76 return nil, err
77 }
78 return encodeModuleBytes(packages), nil
79 }
80
81
82
83
84 func indexPackage(modroot, pkgdir string) []byte {
85 fsys.Trace("indexPackage", pkgdir)
86 p := importRaw(modroot, relPath(pkgdir, modroot))
87 return encodePackageBytes(p)
88 }
89
90
91
92 type rawPackage struct {
93 error string
94 dir string
95
96
97 sourceFiles []*rawFile
98 }
99
100 type parseError struct {
101 ErrorList *scanner.ErrorList
102 ErrorString string
103 }
104
105
106
107
108
109
110 func parseErrorToString(err error) string {
111 if err == nil {
112 return ""
113 }
114 var p parseError
115 if e, ok := err.(scanner.ErrorList); ok {
116 p.ErrorList = &e
117 } else {
118 p.ErrorString = e.Error()
119 }
120 s, err := json.Marshal(p)
121 if err != nil {
122 panic(err)
123 }
124 return string(s)
125 }
126
127
128
129
130
131
132
133 func parseErrorFromString(s string) error {
134 if s == "" {
135 return nil
136 }
137 var p parseError
138 if err := json.Unmarshal([]byte(s), &p); err != nil {
139 base.Fatalf(`go: invalid parse error value in index: %q. This indicates a corrupted index. Run "go clean -cache" to reset the module cache.`, s)
140 }
141 if p.ErrorList != nil {
142 return *p.ErrorList
143 }
144 return errors.New(p.ErrorString)
145 }
146
147
148
149 type rawFile struct {
150 error string
151 parseError string
152
153 name string
154 synopsis string
155 pkgName string
156 ignoreFile bool
157 binaryOnly bool
158 cgoDirectives string
159 goBuildConstraint string
160 plusBuildConstraints []string
161 imports []rawImport
162 embeds []embed
163 directives []build.Directive
164 }
165
166 type rawImport struct {
167 path string
168 position token.Position
169 }
170
171 type embed struct {
172 pattern string
173 position token.Position
174 }
175
176
177
178 func importRaw(modroot, reldir string) *rawPackage {
179 p := &rawPackage{
180 dir: reldir,
181 }
182
183 absdir := filepath.Join(modroot, reldir)
184
185
186
187
188
189
190 if !isDir(absdir) {
191
192 p.error = fmt.Errorf("cannot find package in:\n\t%s", absdir).Error()
193 return p
194 }
195
196 entries, err := fsys.ReadDir(absdir)
197 if err != nil {
198 p.error = err.Error()
199 return p
200 }
201
202 fset := token.NewFileSet()
203 for _, d := range entries {
204 if d.IsDir() {
205 continue
206 }
207 if d.Mode()&fs.ModeSymlink != 0 {
208 if isDir(filepath.Join(absdir, d.Name())) {
209
210 continue
211 }
212 }
213
214 name := d.Name()
215 ext := nameExt(name)
216
217 if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") {
218 continue
219 }
220 info, err := getFileInfo(absdir, name, fset)
221 if err == errNonSource {
222
223 continue
224 } else if err != nil {
225 p.sourceFiles = append(p.sourceFiles, &rawFile{name: name, error: err.Error()})
226 continue
227 } else if info == nil {
228 p.sourceFiles = append(p.sourceFiles, &rawFile{name: name, ignoreFile: true})
229 continue
230 }
231 rf := &rawFile{
232 name: name,
233 goBuildConstraint: info.goBuildConstraint,
234 plusBuildConstraints: info.plusBuildConstraints,
235 binaryOnly: info.binaryOnly,
236 directives: info.directives,
237 }
238 if info.parsed != nil {
239 rf.pkgName = info.parsed.Name.Name
240 }
241
242
243 p.sourceFiles = append(p.sourceFiles, rf)
244 if ext != ".go" {
245 continue
246 }
247
248 if info.parseErr != nil {
249 rf.parseError = parseErrorToString(info.parseErr)
250
251
252 }
253
254 if info.parsed != nil && info.parsed.Doc != nil {
255 rf.synopsis = doc.Synopsis(info.parsed.Doc.Text())
256 }
257
258 var cgoDirectives []string
259 for _, imp := range info.imports {
260 if imp.path == "C" {
261 cgoDirectives = append(cgoDirectives, extractCgoDirectives(imp.doc.Text())...)
262 }
263 rf.imports = append(rf.imports, rawImport{path: imp.path, position: fset.Position(imp.pos)})
264 }
265 rf.cgoDirectives = strings.Join(cgoDirectives, "\n")
266 for _, emb := range info.embeds {
267 rf.embeds = append(rf.embeds, embed{emb.pattern, emb.pos})
268 }
269
270 }
271 return p
272 }
273
274
275
276 func extractCgoDirectives(doc string) []string {
277 var out []string
278 for _, line := range strings.Split(doc, "\n") {
279
280
281
282 line = strings.TrimSpace(line)
283 if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') {
284 continue
285 }
286
287 out = append(out, line)
288 }
289 return out
290 }
291
View as plain text