1
2
3
4
5 package modget
6
7 import (
8 "fmt"
9 "path/filepath"
10 "regexp"
11 "strings"
12 "sync"
13
14 "cmd/go/internal/base"
15 "cmd/go/internal/gover"
16 "cmd/go/internal/modload"
17 "cmd/go/internal/search"
18 "cmd/go/internal/str"
19 "cmd/internal/pkgpattern"
20
21 "golang.org/x/mod/module"
22 )
23
24
25
26 type query struct {
27
28 raw string
29
30
31 rawVersion string
32
33
34
35
36
37
38
39 pattern string
40
41
42
43
44
45
46 patternIsLocal bool
47
48
49
50
51 version string
52
53
54
55
56 matchWildcard func(path string) bool
57
58
59
60
61 canMatchWildcardInModule func(mPath string) bool
62
63
64
65
66 conflict *query
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 candidates []pathSet
86 candidatesMu sync.Mutex
87
88
89
90 pathSeen sync.Map
91
92
93
94
95
96
97
98 resolved []module.Version
99
100
101
102 matchesPackages bool
103 }
104
105
106
107 type pathSet struct {
108
109
110
111
112
113
114
115 path string
116
117
118
119
120
121 pkgMods []module.Version
122
123
124
125
126
127
128
129
130
131 mod module.Version
132
133 err error
134 }
135
136
137 func errSet(err error) pathSet { return pathSet{err: err} }
138
139
140
141 func newQuery(raw string) (*query, error) {
142 pattern, rawVers, found, err := modload.ParsePathVersion(raw)
143 if err != nil {
144 return nil, err
145 }
146 if found && (strings.Contains(rawVers, "@") || rawVers == "") {
147 return nil, fmt.Errorf("invalid module version syntax %q", raw)
148 }
149
150
151
152 version := rawVers
153 if version == "" {
154 if getU.version == "" {
155 version = "upgrade"
156 } else {
157 version = getU.version
158 }
159 }
160
161 q := &query{
162 raw: raw,
163 rawVersion: rawVers,
164 pattern: pattern,
165 patternIsLocal: filepath.IsAbs(pattern) || search.IsRelativePath(pattern),
166 version: version,
167 }
168 if strings.Contains(q.pattern, "...") {
169 q.matchWildcard = pkgpattern.MatchPattern(q.pattern)
170 q.canMatchWildcardInModule = pkgpattern.TreeCanMatchPattern(q.pattern)
171 }
172 if err := q.validate(); err != nil {
173 return q, err
174 }
175 return q, nil
176 }
177
178
179 func (q *query) validate() error {
180 if q.patternIsLocal {
181 if q.rawVersion != "" {
182 return fmt.Errorf("can't request explicit version %q of path %q in main module", q.rawVersion, q.pattern)
183 }
184 return nil
185 }
186
187 if q.pattern == "all" {
188
189 if !modload.HasModRoot() {
190 return fmt.Errorf(`cannot match "all": %v`, modload.ErrNoModRoot)
191 }
192 if !versionOkForMainModule(q.version) {
193
194
195
196 return &modload.QueryUpgradesAllError{
197 MainModules: modload.MainModules.Versions(),
198 Query: q.version,
199 }
200 }
201 }
202
203 if search.IsMetaPackage(q.pattern) && q.pattern != "all" {
204 if q.pattern != q.raw {
205 if q.pattern == "tool" {
206 return fmt.Errorf("can't request explicit version of \"tool\" pattern")
207 }
208 return fmt.Errorf("can't request explicit version of standard-library pattern %q", q.pattern)
209 }
210 }
211
212 return nil
213 }
214
215
216 func (q *query) String() string { return q.raw }
217
218
219 func (q *query) ResolvedString(m module.Version) string {
220 if m.Path != q.pattern {
221 if m.Version != q.version {
222 return fmt.Sprintf("%v (matching %s@%s)", m, q.pattern, q.version)
223 }
224 return fmt.Sprintf("%v (matching %v)", m, q)
225 }
226 if m.Version != q.version {
227 return fmt.Sprintf("%s@%s (%s)", q.pattern, q.version, m.Version)
228 }
229 return q.String()
230 }
231
232
233 func (q *query) isWildcard() bool {
234 return q.matchWildcard != nil || (q.patternIsLocal && strings.Contains(q.pattern, "..."))
235 }
236
237
238 func (q *query) matchesPath(path string) bool {
239 if q.matchWildcard != nil && !gover.IsToolchain(path) {
240 return q.matchWildcard(path)
241 }
242 return path == q.pattern
243 }
244
245
246
247 func (q *query) canMatchInModule(mPath string) bool {
248 if gover.IsToolchain(mPath) {
249 return false
250 }
251 if q.canMatchWildcardInModule != nil {
252 return q.canMatchWildcardInModule(mPath)
253 }
254 return str.HasPathPrefix(q.pattern, mPath)
255 }
256
257
258
259
260
261
262
263
264
265
266 func (q *query) pathOnce(path string, f func() pathSet) {
267 if _, dup := q.pathSeen.LoadOrStore(path, nil); dup {
268 return
269 }
270
271 cs := f()
272
273 if len(cs.pkgMods) > 0 || cs.mod != (module.Version{}) || cs.err != nil {
274 cs.path = path
275 q.candidatesMu.Lock()
276 q.candidates = append(q.candidates, cs)
277 q.candidatesMu.Unlock()
278 }
279 }
280
281
282 func reportError(q *query, err error) {
283 errStr := err.Error()
284
285
286
287
288
289
290
291 patternRE := regexp.MustCompile("(?m)(?:[ \t(\"`]|^)" + regexp.QuoteMeta(q.pattern) + "(?:[ @:;)\"`]|$)")
292 if patternRE.MatchString(errStr) {
293 if q.rawVersion == "" {
294 base.Errorf("go: %s", errStr)
295 return
296 }
297
298 versionRE := regexp.MustCompile("(?m)(?:[ @(\"`]|^)" + regexp.QuoteMeta(q.version) + "(?:[ :;)\"`]|$)")
299 if versionRE.MatchString(errStr) {
300 base.Errorf("go: %s", errStr)
301 return
302 }
303 }
304
305 if qs := q.String(); qs != "" {
306 base.Errorf("go: %s: %s", qs, errStr)
307 } else {
308 base.Errorf("go: %s", errStr)
309 }
310 }
311
312 func reportConflict(pq *query, m module.Version, conflict versionReason) {
313 if pq.conflict != nil {
314
315
316 return
317 }
318 pq.conflict = conflict.reason
319
320 proposed := versionReason{
321 version: m.Version,
322 reason: pq,
323 }
324 if pq.isWildcard() && !conflict.reason.isWildcard() {
325
326 proposed, conflict = conflict, proposed
327 }
328 reportError(pq, &conflictError{
329 mPath: m.Path,
330 proposed: proposed,
331 conflict: conflict,
332 })
333 }
334
335 type conflictError struct {
336 mPath string
337 proposed versionReason
338 conflict versionReason
339 }
340
341 func (e *conflictError) Error() string {
342 argStr := func(q *query, v string) string {
343 if v != q.version {
344 return fmt.Sprintf("%s@%s (%s)", q.pattern, q.version, v)
345 }
346 return q.String()
347 }
348
349 pq := e.proposed.reason
350 rq := e.conflict.reason
351 modDetail := ""
352 if e.mPath != pq.pattern {
353 modDetail = fmt.Sprintf("for module %s, ", e.mPath)
354 }
355
356 return fmt.Sprintf("%s%s conflicts with %s",
357 modDetail,
358 argStr(pq, e.proposed.version),
359 argStr(rq, e.conflict.version))
360 }
361
362 func versionOkForMainModule(version string) bool {
363 return version == "upgrade" || version == "patch"
364 }
365
View as plain text