github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/modload/query.go (about) 1 // Copyright 2018 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 5 package modload 6 7 import ( 8 "bytes" 9 "context" 10 "errors" 11 "fmt" 12 "io/fs" 13 "os" 14 pathpkg "path" 15 "sort" 16 "strings" 17 "sync" 18 "time" 19 20 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg" 21 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/imports" 22 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modfetch" 23 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modfetch/codehost" 24 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modinfo" 25 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/search" 26 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/str" 27 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/trace" 28 "github.com/bir3/gocompiler/src/cmd/internal/pkgpattern" 29 30 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/mod/module" 31 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/mod/semver" 32 ) 33 34 // Query looks up a revision of a given module given a version query string. 35 // The module must be a complete module path. 36 // The version must take one of the following forms: 37 // 38 // - the literal string "latest", denoting the latest available, allowed 39 // tagged version, with non-prereleases preferred over prereleases. 40 // If there are no tagged versions in the repo, latest returns the most 41 // recent commit. 42 // 43 // - the literal string "upgrade", equivalent to "latest" except that if 44 // current is a newer version, current will be returned (see below). 45 // 46 // - the literal string "patch", denoting the latest available tagged version 47 // with the same major and minor number as current (see below). 48 // 49 // - v1, denoting the latest available tagged version v1.x.x. 50 // 51 // - v1.2, denoting the latest available tagged version v1.2.x. 52 // 53 // - v1.2.3, a semantic version string denoting that tagged version. 54 // 55 // - <v1.2.3, <=v1.2.3, >v1.2.3, >=v1.2.3, 56 // denoting the version closest to the target and satisfying the given operator, 57 // with non-prereleases preferred over prereleases. 58 // 59 // - a repository commit identifier or tag, denoting that commit. 60 // 61 // current denotes the currently-selected version of the module; it may be 62 // "none" if no version is currently selected, or "" if the currently-selected 63 // version is unknown or should not be considered. If query is 64 // "upgrade" or "patch", current will be returned if it is a newer 65 // semantic version or a chronologically later pseudo-version than the 66 // version that would otherwise be chosen. This prevents accidental downgrades 67 // from newer pre-release or development versions. 68 // 69 // The allowed function (which may be nil) is used to filter out unsuitable 70 // versions (see AllowedFunc documentation for details). If the query refers to 71 // a specific revision (for example, "master"; see IsRevisionQuery), and the 72 // revision is disallowed by allowed, Query returns the error. If the query 73 // does not refer to a specific revision (for example, "latest"), Query 74 // acts as if versions disallowed by allowed do not exist. 75 // 76 // If path is the path of the main module and the query is "latest", 77 // Query returns Target.Version as the version. 78 // 79 // Query often returns a non-nil *RevInfo with a non-nil error, 80 // to provide an info.Origin that can allow the error to be cached. 81 func Query(ctx context.Context, path, query, current string, allowed AllowedFunc) (*modfetch.RevInfo, error) { 82 ctx, span := trace.StartSpan(ctx, "modload.Query "+path) 83 defer span.Done() 84 85 return queryReuse(ctx, path, query, current, allowed, nil) 86 } 87 88 // queryReuse is like Query but also takes a map of module info that can be reused 89 // if the validation criteria in Origin are met. 90 func queryReuse(ctx context.Context, path, query, current string, allowed AllowedFunc, reuse map[module.Version]*modinfo.ModulePublic) (*modfetch.RevInfo, error) { 91 var info *modfetch.RevInfo 92 err := modfetch.TryProxies(func(proxy string) (err error) { 93 info, err = queryProxy(ctx, proxy, path, query, current, allowed, reuse) 94 return err 95 }) 96 return info, err 97 } 98 99 // checkReuse checks whether a revision of a given module or a version list 100 // for a given module may be reused, according to the information in origin. 101 func checkReuse(ctx context.Context, path string, old *codehost.Origin) error { 102 return modfetch.TryProxies(func(proxy string) error { 103 repo, err := lookupRepo(proxy, path) 104 if err != nil { 105 return err 106 } 107 return repo.CheckReuse(old) 108 }) 109 } 110 111 // AllowedFunc is used by Query and other functions to filter out unsuitable 112 // versions, for example, those listed in exclude directives in the main 113 // module's go.mod file. 114 // 115 // An AllowedFunc returns an error equivalent to ErrDisallowed for an unsuitable 116 // version. Any other error indicates the function was unable to determine 117 // whether the version should be allowed, for example, the function was unable 118 // to fetch or parse a go.mod file containing retractions. Typically, errors 119 // other than ErrDisallowd may be ignored. 120 type AllowedFunc func(context.Context, module.Version) error 121 122 var errQueryDisabled error = queryDisabledError{} 123 124 type queryDisabledError struct{} 125 126 func (queryDisabledError) Error() string { 127 if cfg.BuildModReason == "" { 128 return fmt.Sprintf("cannot query module due to -mod=%s", cfg.BuildMod) 129 } 130 return fmt.Sprintf("cannot query module due to -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason) 131 } 132 133 func queryProxy(ctx context.Context, proxy, path, query, current string, allowed AllowedFunc, reuse map[module.Version]*modinfo.ModulePublic) (*modfetch.RevInfo, error) { 134 ctx, span := trace.StartSpan(ctx, "modload.queryProxy "+path+" "+query) 135 defer span.Done() 136 137 if current != "" && current != "none" && !semver.IsValid(current) { 138 return nil, fmt.Errorf("invalid previous version %q", current) 139 } 140 if cfg.BuildMod == "vendor" { 141 return nil, errQueryDisabled 142 } 143 if allowed == nil { 144 allowed = func(context.Context, module.Version) error { return nil } 145 } 146 147 if MainModules.Contains(path) && (query == "upgrade" || query == "patch") { 148 m := module.Version{Path: path} 149 if err := allowed(ctx, m); err != nil { 150 return nil, fmt.Errorf("internal error: main module version is not allowed: %w", err) 151 } 152 return &modfetch.RevInfo{Version: m.Version}, nil 153 } 154 155 if path == "std" || path == "cmd" { 156 return nil, fmt.Errorf("can't query specific version (%q) of standard-library module %q", query, path) 157 } 158 159 repo, err := lookupRepo(proxy, path) 160 if err != nil { 161 return nil, err 162 } 163 164 if old := reuse[module.Version{Path: path, Version: query}]; old != nil { 165 if err := repo.CheckReuse(old.Origin); err == nil { 166 info := &modfetch.RevInfo{ 167 Version: old.Version, 168 Origin: old.Origin, 169 } 170 if old.Time != nil { 171 info.Time = *old.Time 172 } 173 return info, nil 174 } 175 } 176 177 // Parse query to detect parse errors (and possibly handle query) 178 // before any network I/O. 179 qm, err := newQueryMatcher(path, query, current, allowed) 180 if (err == nil && qm.canStat) || err == errRevQuery { 181 // Direct lookup of a commit identifier or complete (non-prefix) semantic 182 // version. 183 184 // If the identifier is not a canonical semver tag — including if it's a 185 // semver tag with a +metadata suffix — then modfetch.Stat will populate 186 // info.Version with a suitable pseudo-version. 187 info, err := repo.Stat(query) 188 if err != nil { 189 queryErr := err 190 // The full query doesn't correspond to a tag. If it is a semantic version 191 // with a +metadata suffix, see if there is a tag without that suffix: 192 // semantic versioning defines them to be equivalent. 193 canonicalQuery := module.CanonicalVersion(query) 194 if canonicalQuery != "" && query != canonicalQuery { 195 info, err = repo.Stat(canonicalQuery) 196 if err != nil && !errors.Is(err, fs.ErrNotExist) { 197 return info, err 198 } 199 } 200 if err != nil { 201 return info, queryErr 202 } 203 } 204 if err := allowed(ctx, module.Version{Path: path, Version: info.Version}); errors.Is(err, ErrDisallowed) { 205 return nil, err 206 } 207 return info, nil 208 } else if err != nil { 209 return nil, err 210 } 211 212 // Load versions and execute query. 213 versions, err := repo.Versions(qm.prefix) 214 if err != nil { 215 return nil, err 216 } 217 revErr := &modfetch.RevInfo{Origin: versions.Origin} // RevInfo to return with error 218 219 releases, prereleases, err := qm.filterVersions(ctx, versions.List) 220 if err != nil { 221 return revErr, err 222 } 223 224 mergeRevOrigin := func(rev *modfetch.RevInfo, origin *codehost.Origin) *modfetch.RevInfo { 225 merged := mergeOrigin(rev.Origin, origin) 226 if merged == rev.Origin { 227 return rev 228 } 229 clone := new(modfetch.RevInfo) 230 *clone = *rev 231 clone.Origin = merged 232 return clone 233 } 234 235 lookup := func(v string) (*modfetch.RevInfo, error) { 236 rev, err := repo.Stat(v) 237 // Stat can return a non-nil rev and a non-nil err, 238 // in order to provide origin information to make the error cacheable. 239 if rev == nil && err != nil { 240 return revErr, err 241 } 242 rev = mergeRevOrigin(rev, versions.Origin) 243 if err != nil { 244 return rev, err 245 } 246 247 if (query == "upgrade" || query == "patch") && module.IsPseudoVersion(current) && !rev.Time.IsZero() { 248 // Don't allow "upgrade" or "patch" to move from a pseudo-version 249 // to a chronologically older version or pseudo-version. 250 // 251 // If the current version is a pseudo-version from an untagged branch, it 252 // may be semantically lower than the "latest" release or the latest 253 // pseudo-version on the main branch. A user on such a version is unlikely 254 // to intend to “upgrade” to a version that already existed at that point 255 // in time. 256 // 257 // We do this only if the current version is a pseudo-version: if the 258 // version is tagged, the author of the dependency module has given us 259 // explicit information about their intended precedence of this version 260 // relative to other versions, and we shouldn't contradict that 261 // information. (For example, v1.0.1 might be a backport of a fix already 262 // incorporated into v1.1.0, in which case v1.0.1 would be chronologically 263 // newer but v1.1.0 is still an “upgrade”; or v1.0.2 might be a revert of 264 // an unsuccessful fix in v1.0.1, in which case the v1.0.2 commit may be 265 // older than the v1.0.1 commit despite the tag itself being newer.) 266 currentTime, err := module.PseudoVersionTime(current) 267 if err == nil && rev.Time.Before(currentTime) { 268 if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) { 269 return revErr, err 270 } 271 rev, err = repo.Stat(current) 272 if rev == nil && err != nil { 273 return revErr, err 274 } 275 rev = mergeRevOrigin(rev, versions.Origin) 276 return rev, err 277 } 278 } 279 280 return rev, nil 281 } 282 283 if qm.preferLower { 284 if len(releases) > 0 { 285 return lookup(releases[0]) 286 } 287 if len(prereleases) > 0 { 288 return lookup(prereleases[0]) 289 } 290 } else { 291 if len(releases) > 0 { 292 return lookup(releases[len(releases)-1]) 293 } 294 if len(prereleases) > 0 { 295 return lookup(prereleases[len(prereleases)-1]) 296 } 297 } 298 299 if qm.mayUseLatest { 300 latest, err := repo.Latest() 301 if err == nil { 302 if qm.allowsVersion(ctx, latest.Version) { 303 return lookup(latest.Version) 304 } 305 } else if !errors.Is(err, fs.ErrNotExist) { 306 return revErr, err 307 } 308 } 309 310 if (query == "upgrade" || query == "patch") && current != "" && current != "none" { 311 // "upgrade" and "patch" may stay on the current version if allowed. 312 if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) { 313 return nil, err 314 } 315 return lookup(current) 316 } 317 318 return revErr, &NoMatchingVersionError{query: query, current: current} 319 } 320 321 // IsRevisionQuery returns true if vers is a version query that may refer to 322 // a particular version or revision in a repository like "v1.0.0", "master", 323 // or "0123abcd". IsRevisionQuery returns false if vers is a query that 324 // chooses from among available versions like "latest" or ">v1.0.0". 325 func IsRevisionQuery(vers string) bool { 326 if vers == "latest" || 327 vers == "upgrade" || 328 vers == "patch" || 329 strings.HasPrefix(vers, "<") || 330 strings.HasPrefix(vers, ">") || 331 (semver.IsValid(vers) && isSemverPrefix(vers)) { 332 return false 333 } 334 return true 335 } 336 337 // isSemverPrefix reports whether v is a semantic version prefix: v1 or v1.2 (not v1.2.3). 338 // The caller is assumed to have checked that semver.IsValid(v) is true. 339 func isSemverPrefix(v string) bool { 340 dots := 0 341 for i := 0; i < len(v); i++ { 342 switch v[i] { 343 case '-', '+': 344 return false 345 case '.': 346 dots++ 347 if dots >= 2 { 348 return false 349 } 350 } 351 } 352 return true 353 } 354 355 type queryMatcher struct { 356 path string 357 prefix string 358 filter func(version string) bool 359 allowed AllowedFunc 360 canStat bool // if true, the query can be resolved by repo.Stat 361 preferLower bool // if true, choose the lowest matching version 362 mayUseLatest bool 363 preferIncompatible bool 364 } 365 366 var errRevQuery = errors.New("query refers to a non-semver revision") 367 368 // newQueryMatcher returns a new queryMatcher that matches the versions 369 // specified by the given query on the module with the given path. 370 // 371 // If the query can only be resolved by statting a non-SemVer revision, 372 // newQueryMatcher returns errRevQuery. 373 func newQueryMatcher(path string, query, current string, allowed AllowedFunc) (*queryMatcher, error) { 374 badVersion := func(v string) (*queryMatcher, error) { 375 return nil, fmt.Errorf("invalid semantic version %q in range %q", v, query) 376 } 377 378 matchesMajor := func(v string) bool { 379 _, pathMajor, ok := module.SplitPathVersion(path) 380 if !ok { 381 return false 382 } 383 return module.CheckPathMajor(v, pathMajor) == nil 384 } 385 386 qm := &queryMatcher{ 387 path: path, 388 allowed: allowed, 389 preferIncompatible: strings.HasSuffix(current, "+incompatible"), 390 } 391 392 switch { 393 case query == "latest": 394 qm.mayUseLatest = true 395 396 case query == "upgrade": 397 if current == "" || current == "none" { 398 qm.mayUseLatest = true 399 } else { 400 qm.mayUseLatest = module.IsPseudoVersion(current) 401 qm.filter = func(mv string) bool { return semver.Compare(mv, current) >= 0 } 402 } 403 404 case query == "patch": 405 if current == "" || current == "none" { 406 return nil, &NoPatchBaseError{path} 407 } 408 if current == "" { 409 qm.mayUseLatest = true 410 } else { 411 qm.mayUseLatest = module.IsPseudoVersion(current) 412 qm.prefix = semver.MajorMinor(current) + "." 413 qm.filter = func(mv string) bool { return semver.Compare(mv, current) >= 0 } 414 } 415 416 case strings.HasPrefix(query, "<="): 417 v := query[len("<="):] 418 if !semver.IsValid(v) { 419 return badVersion(v) 420 } 421 if isSemverPrefix(v) { 422 // Refuse to say whether <=v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3). 423 return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query) 424 } 425 qm.filter = func(mv string) bool { return semver.Compare(mv, v) <= 0 } 426 if !matchesMajor(v) { 427 qm.preferIncompatible = true 428 } 429 430 case strings.HasPrefix(query, "<"): 431 v := query[len("<"):] 432 if !semver.IsValid(v) { 433 return badVersion(v) 434 } 435 qm.filter = func(mv string) bool { return semver.Compare(mv, v) < 0 } 436 if !matchesMajor(v) { 437 qm.preferIncompatible = true 438 } 439 440 case strings.HasPrefix(query, ">="): 441 v := query[len(">="):] 442 if !semver.IsValid(v) { 443 return badVersion(v) 444 } 445 qm.filter = func(mv string) bool { return semver.Compare(mv, v) >= 0 } 446 qm.preferLower = true 447 if !matchesMajor(v) { 448 qm.preferIncompatible = true 449 } 450 451 case strings.HasPrefix(query, ">"): 452 v := query[len(">"):] 453 if !semver.IsValid(v) { 454 return badVersion(v) 455 } 456 if isSemverPrefix(v) { 457 // Refuse to say whether >v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3). 458 return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query) 459 } 460 qm.filter = func(mv string) bool { return semver.Compare(mv, v) > 0 } 461 qm.preferLower = true 462 if !matchesMajor(v) { 463 qm.preferIncompatible = true 464 } 465 466 case semver.IsValid(query): 467 if isSemverPrefix(query) { 468 qm.prefix = query + "." 469 // Do not allow the query "v1.2" to match versions lower than "v1.2.0", 470 // such as prereleases for that version. (https://golang.org/issue/31972) 471 qm.filter = func(mv string) bool { return semver.Compare(mv, query) >= 0 } 472 } else { 473 qm.canStat = true 474 qm.filter = func(mv string) bool { return semver.Compare(mv, query) == 0 } 475 qm.prefix = semver.Canonical(query) 476 } 477 if !matchesMajor(query) { 478 qm.preferIncompatible = true 479 } 480 481 default: 482 return nil, errRevQuery 483 } 484 485 return qm, nil 486 } 487 488 // allowsVersion reports whether version v is allowed by the prefix, filter, and 489 // AllowedFunc of qm. 490 func (qm *queryMatcher) allowsVersion(ctx context.Context, v string) bool { 491 if qm.prefix != "" && !strings.HasPrefix(v, qm.prefix) { 492 return false 493 } 494 if qm.filter != nil && !qm.filter(v) { 495 return false 496 } 497 if qm.allowed != nil { 498 if err := qm.allowed(ctx, module.Version{Path: qm.path, Version: v}); errors.Is(err, ErrDisallowed) { 499 return false 500 } 501 } 502 return true 503 } 504 505 // filterVersions classifies versions into releases and pre-releases, filtering 506 // out: 507 // 1. versions that do not satisfy the 'allowed' predicate, and 508 // 2. "+incompatible" versions, if a compatible one satisfies the predicate 509 // and the incompatible version is not preferred. 510 // 511 // If the allowed predicate returns an error not equivalent to ErrDisallowed, 512 // filterVersions returns that error. 513 func (qm *queryMatcher) filterVersions(ctx context.Context, versions []string) (releases, prereleases []string, err error) { 514 needIncompatible := qm.preferIncompatible 515 516 var lastCompatible string 517 for _, v := range versions { 518 if !qm.allowsVersion(ctx, v) { 519 continue 520 } 521 522 if !needIncompatible { 523 // We're not yet sure whether we need to include +incomptaible versions. 524 // Keep track of the last compatible version we've seen, and use the 525 // presence (or absence) of a go.mod file in that version to decide: a 526 // go.mod file implies that the module author is supporting modules at a 527 // compatible version (and we should ignore +incompatible versions unless 528 // requested explicitly), while a lack of go.mod file implies the 529 // potential for legacy (pre-modules) versioning without semantic import 530 // paths (and thus *with* +incompatible versions). 531 // 532 // This isn't strictly accurate if the latest compatible version has been 533 // replaced by a local file path, because we do not allow file-path 534 // replacements without a go.mod file: the user would have needed to add 535 // one. However, replacing the last compatible version while 536 // simultaneously expecting to upgrade implicitly to a +incompatible 537 // version seems like an extreme enough corner case to ignore for now. 538 539 if !strings.HasSuffix(v, "+incompatible") { 540 lastCompatible = v 541 } else if lastCompatible != "" { 542 // If the latest compatible version is allowed and has a go.mod file, 543 // ignore any version with a higher (+incompatible) major version. (See 544 // https://golang.org/issue/34165.) Note that we even prefer a 545 // compatible pre-release over an incompatible release. 546 ok, err := versionHasGoMod(ctx, module.Version{Path: qm.path, Version: lastCompatible}) 547 if err != nil { 548 return nil, nil, err 549 } 550 if ok { 551 // The last compatible version has a go.mod file, so that's the 552 // highest version we're willing to consider. Don't bother even 553 // looking at higher versions, because they're all +incompatible from 554 // here onward. 555 break 556 } 557 558 // No acceptable compatible release has a go.mod file, so the versioning 559 // for the module might not be module-aware, and we should respect 560 // legacy major-version tags. 561 needIncompatible = true 562 } 563 } 564 565 if semver.Prerelease(v) != "" { 566 prereleases = append(prereleases, v) 567 } else { 568 releases = append(releases, v) 569 } 570 } 571 572 return releases, prereleases, nil 573 } 574 575 type QueryResult struct { 576 Mod module.Version 577 Rev *modfetch.RevInfo 578 Packages []string 579 } 580 581 // QueryPackages is like QueryPattern, but requires that the pattern match at 582 // least one package and omits the non-package result (if any). 583 func QueryPackages(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) ([]QueryResult, error) { 584 pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed) 585 586 if len(pkgMods) == 0 && err == nil { 587 replacement := Replacement(modOnly.Mod) 588 return nil, &PackageNotInModuleError{ 589 Mod: modOnly.Mod, 590 Replacement: replacement, 591 Query: query, 592 Pattern: pattern, 593 } 594 } 595 596 return pkgMods, err 597 } 598 599 // QueryPattern looks up the module(s) containing at least one package matching 600 // the given pattern at the given version. The results are sorted by module path 601 // length in descending order. If any proxy provides a non-empty set of candidate 602 // modules, no further proxies are tried. 603 // 604 // For wildcard patterns, QueryPattern looks in modules with package paths up to 605 // the first "..." in the pattern. For the pattern "example.com/a/b.../c", 606 // QueryPattern would consider prefixes of "example.com/a". 607 // 608 // If any matching package is in the main module, QueryPattern considers only 609 // the main module and only the version "latest", without checking for other 610 // possible modules. 611 // 612 // QueryPattern always returns at least one QueryResult (which may be only 613 // modOnly) or a non-nil error. 614 func QueryPattern(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) (pkgMods []QueryResult, modOnly *QueryResult, err error) { 615 ctx, span := trace.StartSpan(ctx, "modload.QueryPattern "+pattern+" "+query) 616 defer span.Done() 617 618 base := pattern 619 620 firstError := func(m *search.Match) error { 621 if len(m.Errs) == 0 { 622 return nil 623 } 624 return m.Errs[0] 625 } 626 627 var match func(mod module.Version, roots []string, isLocal bool) *search.Match 628 matchPattern := pkgpattern.MatchPattern(pattern) 629 630 if i := strings.Index(pattern, "..."); i >= 0 { 631 base = pathpkg.Dir(pattern[:i+3]) 632 if base == "." { 633 return nil, nil, &WildcardInFirstElementError{Pattern: pattern, Query: query} 634 } 635 match = func(mod module.Version, roots []string, isLocal bool) *search.Match { 636 m := search.NewMatch(pattern) 637 matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{mod}) 638 return m 639 } 640 } else { 641 match = func(mod module.Version, roots []string, isLocal bool) *search.Match { 642 m := search.NewMatch(pattern) 643 prefix := mod.Path 644 if MainModules.Contains(mod.Path) { 645 prefix = MainModules.PathPrefix(module.Version{Path: mod.Path}) 646 } 647 for _, root := range roots { 648 if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil { 649 m.AddError(err) 650 } else if ok { 651 m.Pkgs = []string{pattern} 652 } 653 } 654 return m 655 } 656 } 657 658 var mainModuleMatches []module.Version 659 for _, mainModule := range MainModules.Versions() { 660 m := match(mainModule, modRoots, true) 661 if len(m.Pkgs) > 0 { 662 if query != "upgrade" && query != "patch" { 663 return nil, nil, &QueryMatchesPackagesInMainModuleError{ 664 Pattern: pattern, 665 Query: query, 666 Packages: m.Pkgs, 667 } 668 } 669 if err := allowed(ctx, mainModule); err != nil { 670 return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, mainModule.Path, err) 671 } 672 return []QueryResult{{ 673 Mod: mainModule, 674 Rev: &modfetch.RevInfo{Version: mainModule.Version}, 675 Packages: m.Pkgs, 676 }}, nil, nil 677 } 678 if err := firstError(m); err != nil { 679 return nil, nil, err 680 } 681 682 var matchesMainModule bool 683 if matchPattern(mainModule.Path) { 684 mainModuleMatches = append(mainModuleMatches, mainModule) 685 matchesMainModule = true 686 } 687 688 if (query == "upgrade" || query == "patch") && matchesMainModule { 689 if err := allowed(ctx, mainModule); err == nil { 690 modOnly = &QueryResult{ 691 Mod: mainModule, 692 Rev: &modfetch.RevInfo{Version: mainModule.Version}, 693 } 694 } 695 } 696 } 697 698 var ( 699 results []QueryResult 700 candidateModules = modulePrefixesExcludingTarget(base) 701 ) 702 if len(candidateModules) == 0 { 703 if modOnly != nil { 704 return nil, modOnly, nil 705 } else if len(mainModuleMatches) != 0 { 706 return nil, nil, &QueryMatchesMainModulesError{ 707 MainModules: mainModuleMatches, 708 Pattern: pattern, 709 Query: query, 710 } 711 } else { 712 return nil, nil, &PackageNotInModuleError{ 713 MainModules: mainModuleMatches, 714 Query: query, 715 Pattern: pattern, 716 } 717 } 718 } 719 720 err = modfetch.TryProxies(func(proxy string) error { 721 queryModule := func(ctx context.Context, path string) (r QueryResult, err error) { 722 ctx, span := trace.StartSpan(ctx, "modload.QueryPattern.queryModule ["+proxy+"] "+path) 723 defer span.Done() 724 725 pathCurrent := current(path) 726 r.Mod.Path = path 727 r.Rev, err = queryProxy(ctx, proxy, path, query, pathCurrent, allowed, nil) 728 if err != nil { 729 return r, err 730 } 731 r.Mod.Version = r.Rev.Version 732 root, isLocal, err := fetch(ctx, r.Mod) 733 if err != nil { 734 return r, err 735 } 736 m := match(r.Mod, []string{root}, isLocal) 737 r.Packages = m.Pkgs 738 if len(r.Packages) == 0 && !matchPattern(path) { 739 if err := firstError(m); err != nil { 740 return r, err 741 } 742 replacement := Replacement(r.Mod) 743 return r, &PackageNotInModuleError{ 744 Mod: r.Mod, 745 Replacement: replacement, 746 Query: query, 747 Pattern: pattern, 748 } 749 } 750 return r, nil 751 } 752 753 allResults, err := queryPrefixModules(ctx, candidateModules, queryModule) 754 results = allResults[:0] 755 for _, r := range allResults { 756 if len(r.Packages) == 0 { 757 modOnly = &r 758 } else { 759 results = append(results, r) 760 } 761 } 762 return err 763 }) 764 765 if len(mainModuleMatches) > 0 && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) { 766 return nil, nil, &QueryMatchesMainModulesError{ 767 Pattern: pattern, 768 Query: query, 769 } 770 } 771 return results[:len(results):len(results)], modOnly, err 772 } 773 774 // modulePrefixesExcludingTarget returns all prefixes of path that may plausibly 775 // exist as a module, excluding targetPrefix but otherwise including path 776 // itself, sorted by descending length. Prefixes that are not valid module paths 777 // but are valid package paths (like "m" or "example.com/.gen") are included, 778 // since they might be replaced. 779 func modulePrefixesExcludingTarget(path string) []string { 780 prefixes := make([]string, 0, strings.Count(path, "/")+1) 781 782 mainModulePrefixes := make(map[string]bool) 783 for _, m := range MainModules.Versions() { 784 mainModulePrefixes[m.Path] = true 785 } 786 787 for { 788 if !mainModulePrefixes[path] { 789 if _, _, ok := module.SplitPathVersion(path); ok { 790 prefixes = append(prefixes, path) 791 } 792 } 793 794 j := strings.LastIndexByte(path, '/') 795 if j < 0 { 796 break 797 } 798 path = path[:j] 799 } 800 801 return prefixes 802 } 803 804 func queryPrefixModules(ctx context.Context, candidateModules []string, queryModule func(ctx context.Context, path string) (QueryResult, error)) (found []QueryResult, err error) { 805 ctx, span := trace.StartSpan(ctx, "modload.queryPrefixModules") 806 defer span.Done() 807 808 // If the path we're attempting is not in the module cache and we don't have a 809 // fetch result cached either, we'll end up making a (potentially slow) 810 // request to the proxy or (often even slower) the origin server. 811 // To minimize latency, execute all of those requests in parallel. 812 type result struct { 813 QueryResult 814 err error 815 } 816 results := make([]result, len(candidateModules)) 817 var wg sync.WaitGroup 818 wg.Add(len(candidateModules)) 819 for i, p := range candidateModules { 820 ctx := trace.StartGoroutine(ctx) 821 go func(p string, r *result) { 822 r.QueryResult, r.err = queryModule(ctx, p) 823 wg.Done() 824 }(p, &results[i]) 825 } 826 wg.Wait() 827 828 // Classify the results. In case of failure, identify the error that the user 829 // is most likely to find helpful: the most useful class of error at the 830 // longest matching path. 831 var ( 832 noPackage *PackageNotInModuleError 833 noVersion *NoMatchingVersionError 834 noPatchBase *NoPatchBaseError 835 invalidPath *module.InvalidPathError // see comment in case below 836 notExistErr error 837 ) 838 for _, r := range results { 839 switch rErr := r.err.(type) { 840 case nil: 841 found = append(found, r.QueryResult) 842 case *PackageNotInModuleError: 843 // Given the option, prefer to attribute “package not in module” 844 // to modules other than the main one. 845 if noPackage == nil || MainModules.Contains(noPackage.Mod.Path) { 846 noPackage = rErr 847 } 848 case *NoMatchingVersionError: 849 if noVersion == nil { 850 noVersion = rErr 851 } 852 case *NoPatchBaseError: 853 if noPatchBase == nil { 854 noPatchBase = rErr 855 } 856 case *module.InvalidPathError: 857 // The prefix was not a valid module path, and there was no replacement. 858 // Prefixes like this may appear in candidateModules, since we handle 859 // replaced modules that weren't required in the repo lookup process 860 // (see lookupRepo). 861 // 862 // A shorter prefix may be a valid module path and may contain a valid 863 // import path, so this is a low-priority error. 864 if invalidPath == nil { 865 invalidPath = rErr 866 } 867 default: 868 if errors.Is(rErr, fs.ErrNotExist) { 869 if notExistErr == nil { 870 notExistErr = rErr 871 } 872 } else if err == nil { 873 if len(found) > 0 || noPackage != nil { 874 // golang.org/issue/34094: If we have already found a module that 875 // could potentially contain the target package, ignore unclassified 876 // errors for modules with shorter paths. 877 878 // golang.org/issue/34383 is a special case of this: if we have 879 // already found example.com/foo/v2@v2.0.0 with a matching go.mod 880 // file, ignore the error from example.com/foo@v2.0.0. 881 } else { 882 err = r.err 883 } 884 } 885 } 886 } 887 888 // TODO(#26232): If len(found) == 0 and some of the errors are 4xx HTTP 889 // codes, have the auth package recheck the failed paths. 890 // If we obtain new credentials for any of them, re-run the above loop. 891 892 if len(found) == 0 && err == nil { 893 switch { 894 case noPackage != nil: 895 err = noPackage 896 case noVersion != nil: 897 err = noVersion 898 case noPatchBase != nil: 899 err = noPatchBase 900 case invalidPath != nil: 901 err = invalidPath 902 case notExistErr != nil: 903 err = notExistErr 904 default: 905 panic("queryPrefixModules: no modules found, but no error detected") 906 } 907 } 908 909 return found, err 910 } 911 912 // A NoMatchingVersionError indicates that Query found a module at the requested 913 // path, but not at any versions satisfying the query string and allow-function. 914 // 915 // NOTE: NoMatchingVersionError MUST NOT implement Is(fs.ErrNotExist). 916 // 917 // If the module came from a proxy, that proxy had to return a successful status 918 // code for the versions it knows about, and thus did not have the opportunity 919 // to return a non-400 status code to suppress fallback. 920 type NoMatchingVersionError struct { 921 query, current string 922 } 923 924 func (e *NoMatchingVersionError) Error() string { 925 currentSuffix := "" 926 if (e.query == "upgrade" || e.query == "patch") && e.current != "" && e.current != "none" { 927 currentSuffix = fmt.Sprintf(" (current version is %s)", e.current) 928 } 929 return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix 930 } 931 932 // A NoPatchBaseError indicates that Query was called with the query "patch" 933 // but with a current version of "" or "none". 934 type NoPatchBaseError struct { 935 path string 936 } 937 938 func (e *NoPatchBaseError) Error() string { 939 return fmt.Sprintf(`can't query version "patch" of module %s: no existing version is required`, e.path) 940 } 941 942 // A WildcardInFirstElementError indicates that a pattern passed to QueryPattern 943 // had a wildcard in its first path element, and therefore had no pattern-prefix 944 // modules to search in. 945 type WildcardInFirstElementError struct { 946 Pattern string 947 Query string 948 } 949 950 func (e *WildcardInFirstElementError) Error() string { 951 return fmt.Sprintf("no modules to query for %s@%s because first path element contains a wildcard", e.Pattern, e.Query) 952 } 953 954 // A PackageNotInModuleError indicates that QueryPattern found a candidate 955 // module at the requested version, but that module did not contain any packages 956 // matching the requested pattern. 957 // 958 // NOTE: PackageNotInModuleError MUST NOT implement Is(fs.ErrNotExist). 959 // 960 // If the module came from a proxy, that proxy had to return a successful status 961 // code for the versions it knows about, and thus did not have the opportunity 962 // to return a non-400 status code to suppress fallback. 963 type PackageNotInModuleError struct { 964 MainModules []module.Version 965 Mod module.Version 966 Replacement module.Version 967 Query string 968 Pattern string 969 } 970 971 func (e *PackageNotInModuleError) Error() string { 972 if len(e.MainModules) > 0 { 973 prefix := "workspace modules do" 974 if len(e.MainModules) == 1 { 975 prefix = fmt.Sprintf("main module (%s) does", e.MainModules[0]) 976 } 977 if strings.Contains(e.Pattern, "...") { 978 return fmt.Sprintf("%s not contain packages matching %s", prefix, e.Pattern) 979 } 980 return fmt.Sprintf("%s not contain package %s", prefix, e.Pattern) 981 } 982 983 found := "" 984 if r := e.Replacement; r.Path != "" { 985 replacement := r.Path 986 if r.Version != "" { 987 replacement = fmt.Sprintf("%s@%s", r.Path, r.Version) 988 } 989 if e.Query == e.Mod.Version { 990 found = fmt.Sprintf(" (replaced by %s)", replacement) 991 } else { 992 found = fmt.Sprintf(" (%s, replaced by %s)", e.Mod.Version, replacement) 993 } 994 } else if e.Query != e.Mod.Version { 995 found = fmt.Sprintf(" (%s)", e.Mod.Version) 996 } 997 998 if strings.Contains(e.Pattern, "...") { 999 return fmt.Sprintf("module %s@%s found%s, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern) 1000 } 1001 return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern) 1002 } 1003 1004 func (e *PackageNotInModuleError) ImportPath() string { 1005 if !strings.Contains(e.Pattern, "...") { 1006 return e.Pattern 1007 } 1008 return "" 1009 } 1010 1011 // versionHasGoMod returns whether a version has a go.mod file. 1012 // 1013 // versionHasGoMod fetches the go.mod file (possibly a fake) and true if it 1014 // contains anything other than a module directive with the same path. When a 1015 // module does not have a real go.mod file, the go command acts as if it had one 1016 // that only contained a module directive. Normal go.mod files created after 1017 // 1.12 at least have a go directive. 1018 // 1019 // This function is a heuristic, since it's possible to commit a file that would 1020 // pass this test. However, we only need a heurstic for determining whether 1021 // +incompatible versions may be "latest", which is what this function is used 1022 // for. 1023 // 1024 // This heuristic is useful for two reasons: first, when using a proxy, 1025 // this lets us fetch from the .mod endpoint which is much faster than the .zip 1026 // endpoint. The .mod file is used anyway, even if the .zip file contains a 1027 // go.mod with different content. Second, if we don't fetch the .zip, then 1028 // we don't need to verify it in go.sum. This makes 'go list -m -u' faster 1029 // and simpler. 1030 func versionHasGoMod(_ context.Context, m module.Version) (bool, error) { 1031 _, data, err := rawGoModData(m) 1032 if err != nil { 1033 return false, err 1034 } 1035 isFake := bytes.Equal(data, modfetch.LegacyGoMod(m.Path)) 1036 return !isFake, nil 1037 } 1038 1039 // A versionRepo is a subset of modfetch.Repo that can report information about 1040 // available versions, but cannot fetch specific source files. 1041 type versionRepo interface { 1042 ModulePath() string 1043 CheckReuse(*codehost.Origin) error 1044 Versions(prefix string) (*modfetch.Versions, error) 1045 Stat(rev string) (*modfetch.RevInfo, error) 1046 Latest() (*modfetch.RevInfo, error) 1047 } 1048 1049 var _ versionRepo = modfetch.Repo(nil) 1050 1051 func lookupRepo(proxy, path string) (repo versionRepo, err error) { 1052 err = module.CheckPath(path) 1053 if err == nil { 1054 repo = modfetch.Lookup(proxy, path) 1055 } else { 1056 repo = emptyRepo{path: path, err: err} 1057 } 1058 1059 if MainModules == nil { 1060 return repo, err 1061 } else if _, ok := MainModules.HighestReplaced()[path]; ok { 1062 return &replacementRepo{repo: repo}, nil 1063 } 1064 1065 return repo, err 1066 } 1067 1068 // An emptyRepo is a versionRepo that contains no versions. 1069 type emptyRepo struct { 1070 path string 1071 err error 1072 } 1073 1074 var _ versionRepo = emptyRepo{} 1075 1076 func (er emptyRepo) ModulePath() string { return er.path } 1077 func (er emptyRepo) CheckReuse(old *codehost.Origin) error { 1078 return fmt.Errorf("empty repo") 1079 } 1080 func (er emptyRepo) Versions(prefix string) (*modfetch.Versions, error) { 1081 return &modfetch.Versions{}, nil 1082 } 1083 func (er emptyRepo) Stat(rev string) (*modfetch.RevInfo, error) { return nil, er.err } 1084 func (er emptyRepo) Latest() (*modfetch.RevInfo, error) { return nil, er.err } 1085 1086 // A replacementRepo augments a versionRepo to include the replacement versions 1087 // (if any) found in the main module's go.mod file. 1088 // 1089 // A replacementRepo suppresses "not found" errors for otherwise-nonexistent 1090 // modules, so a replacementRepo should only be constructed for a module that 1091 // actually has one or more valid replacements. 1092 type replacementRepo struct { 1093 repo versionRepo 1094 } 1095 1096 var _ versionRepo = (*replacementRepo)(nil) 1097 1098 func (rr *replacementRepo) ModulePath() string { return rr.repo.ModulePath() } 1099 1100 func (rr *replacementRepo) CheckReuse(old *codehost.Origin) error { 1101 return fmt.Errorf("replacement repo") 1102 } 1103 1104 // Versions returns the versions from rr.repo augmented with any matching 1105 // replacement versions. 1106 func (rr *replacementRepo) Versions(prefix string) (*modfetch.Versions, error) { 1107 repoVersions, err := rr.repo.Versions(prefix) 1108 if err != nil { 1109 if !errors.Is(err, os.ErrNotExist) { 1110 return nil, err 1111 } 1112 repoVersions = new(modfetch.Versions) 1113 } 1114 1115 versions := repoVersions.List 1116 for _, mm := range MainModules.Versions() { 1117 if index := MainModules.Index(mm); index != nil && len(index.replace) > 0 { 1118 path := rr.ModulePath() 1119 for m := range index.replace { 1120 if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) { 1121 versions = append(versions, m.Version) 1122 } 1123 } 1124 } 1125 } 1126 1127 if len(versions) == len(repoVersions.List) { // replacement versions added 1128 return repoVersions, nil 1129 } 1130 1131 sort.Slice(versions, func(i, j int) bool { 1132 return semver.Compare(versions[i], versions[j]) < 0 1133 }) 1134 str.Uniq(&versions) 1135 return &modfetch.Versions{List: versions}, nil 1136 } 1137 1138 func (rr *replacementRepo) Stat(rev string) (*modfetch.RevInfo, error) { 1139 info, err := rr.repo.Stat(rev) 1140 if err == nil { 1141 return info, err 1142 } 1143 var hasReplacements bool 1144 for _, v := range MainModules.Versions() { 1145 if index := MainModules.Index(v); index != nil && len(index.replace) > 0 { 1146 hasReplacements = true 1147 } 1148 } 1149 if !hasReplacements { 1150 return info, err 1151 } 1152 1153 v := module.CanonicalVersion(rev) 1154 if v != rev { 1155 // The replacements in the go.mod file list only canonical semantic versions, 1156 // so a non-canonical version can't possibly have a replacement. 1157 return info, err 1158 } 1159 1160 path := rr.ModulePath() 1161 _, pathMajor, ok := module.SplitPathVersion(path) 1162 if ok && pathMajor == "" { 1163 if err := module.CheckPathMajor(v, pathMajor); err != nil && semver.Build(v) == "" { 1164 v += "+incompatible" 1165 } 1166 } 1167 1168 if r := Replacement(module.Version{Path: path, Version: v}); r.Path == "" { 1169 return info, err 1170 } 1171 return rr.replacementStat(v) 1172 } 1173 1174 func (rr *replacementRepo) Latest() (*modfetch.RevInfo, error) { 1175 info, err := rr.repo.Latest() 1176 path := rr.ModulePath() 1177 1178 if v, ok := MainModules.HighestReplaced()[path]; ok { 1179 if v == "" { 1180 // The only replacement is a wildcard that doesn't specify a version, so 1181 // synthesize a pseudo-version with an appropriate major version and a 1182 // timestamp below any real timestamp. That way, if the main module is 1183 // used from within some other module, the user will be able to upgrade 1184 // the requirement to any real version they choose. 1185 if _, pathMajor, ok := module.SplitPathVersion(path); ok && len(pathMajor) > 0 { 1186 v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000") 1187 } else { 1188 v = module.PseudoVersion("v0", "", time.Time{}, "000000000000") 1189 } 1190 } 1191 1192 if err != nil || semver.Compare(v, info.Version) > 0 { 1193 return rr.replacementStat(v) 1194 } 1195 } 1196 1197 return info, err 1198 } 1199 1200 func (rr *replacementRepo) replacementStat(v string) (*modfetch.RevInfo, error) { 1201 rev := &modfetch.RevInfo{Version: v} 1202 if module.IsPseudoVersion(v) { 1203 rev.Time, _ = module.PseudoVersionTime(v) 1204 rev.Short, _ = module.PseudoVersionRev(v) 1205 } 1206 return rev, nil 1207 } 1208 1209 // A QueryMatchesMainModulesError indicates that a query requests 1210 // a version of the main module that cannot be satisfied. 1211 // (The main module's version cannot be changed.) 1212 type QueryMatchesMainModulesError struct { 1213 MainModules []module.Version 1214 Pattern string 1215 Query string 1216 } 1217 1218 func (e *QueryMatchesMainModulesError) Error() string { 1219 if MainModules.Contains(e.Pattern) { 1220 return fmt.Sprintf("can't request version %q of the main module (%s)", e.Query, e.Pattern) 1221 } 1222 1223 plural := "" 1224 mainModulePaths := make([]string, len(e.MainModules)) 1225 for i := range e.MainModules { 1226 mainModulePaths[i] = e.MainModules[i].Path 1227 } 1228 if len(e.MainModules) > 1 { 1229 plural = "s" 1230 } 1231 return fmt.Sprintf("can't request version %q of pattern %q that includes the main module%s (%s)", e.Query, e.Pattern, plural, strings.Join(mainModulePaths, ", ")) 1232 } 1233 1234 // A QueryUpgradesAllError indicates that a query requests 1235 // an upgrade on the all pattern. 1236 // (The main module's version cannot be changed.) 1237 type QueryUpgradesAllError struct { 1238 MainModules []module.Version 1239 Query string 1240 } 1241 1242 func (e *QueryUpgradesAllError) Error() string { 1243 var plural string = "" 1244 if len(e.MainModules) != 1 { 1245 plural = "s" 1246 } 1247 1248 return fmt.Sprintf("can't request version %q of pattern \"all\" that includes the main module%s", e.Query, plural) 1249 } 1250 1251 // A QueryMatchesPackagesInMainModuleError indicates that a query cannot be 1252 // satisfied because it matches one or more packages found in the main module. 1253 type QueryMatchesPackagesInMainModuleError struct { 1254 Pattern string 1255 Query string 1256 Packages []string 1257 } 1258 1259 func (e *QueryMatchesPackagesInMainModuleError) Error() string { 1260 if len(e.Packages) > 1 { 1261 return fmt.Sprintf("pattern %s matches %d packages in the main module, so can't request version %s", e.Pattern, len(e.Packages), e.Query) 1262 } 1263 1264 if search.IsMetaPackage(e.Pattern) || strings.Contains(e.Pattern, "...") { 1265 return fmt.Sprintf("pattern %s matches package %s in the main module, so can't request version %s", e.Pattern, e.Packages[0], e.Query) 1266 } 1267 1268 return fmt.Sprintf("package %s is in the main module, so can't request version %s", e.Packages[0], e.Query) 1269 }