src/cmd/go/internal/modget/get.go GO 2,159 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,159.
1// Copyright 2018 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45// Package modget implements the module-aware “go get” command.6package modget78// The arguments to 'go get' are patterns with optional version queries, with9// the version queries defaulting to "upgrade".10//11// The patterns are normally interpreted as package patterns. However, if a12// pattern cannot match a package, it is instead interpreted as a *module*13// pattern. For version queries such as "upgrade" and "patch" that depend on the14// selected version of a module (or of the module containing a package),15// whether a pattern denotes a package or module may change as updates are16// applied (see the example in mod_get_patchmod.txt).17//18// There are a few other ambiguous cases to resolve, too. A package can exist in19// two different modules at the same version: for example, the package20// example.com/foo might be found in module example.com and also in module21// example.com/foo, and those modules may have independent v0.1.0 tags — so the22// input 'example.com/foo@v0.1.0' could syntactically refer to the variant of23// the package loaded from either module! (See mod_get_ambiguous_pkg.txt.)24// If the argument is ambiguous, the user can often disambiguate by specifying25// explicit versions for *all* of the potential module paths involved.2627import (28	"context"29	"errors"30	"fmt"31	"os"32	"path/filepath"33	"runtime"34	"sort"35	"strconv"36	"strings"37	"sync"3839	"cmd/go/internal/base"40	"cmd/go/internal/cfg"41	"cmd/go/internal/gover"42	"cmd/go/internal/imports"43	"cmd/go/internal/modfetch"44	"cmd/go/internal/modload"45	"cmd/go/internal/search"46	"cmd/go/internal/toolchain"47	"cmd/go/internal/work"48	"cmd/internal/par"4950	"golang.org/x/mod/modfile"51	"golang.org/x/mod/module"52)5354var CmdGet = &base.Command{55	// Note: flags below are listed explicitly because they're the most common.56	// Do not send CLs removing them because they're covered by [get flags].57	UsageLine: "go get [-t] [-u] [-tool] [build flags] [packages]",58	Short:     "add dependencies to current module and install them",59	Long: `60Get resolves its command-line arguments to packages at specific module versions,61updates go.mod to require those versions, and downloads source code into the62module cache.6364To add a dependency for a package or upgrade it to its latest version:6566	go get example.com/pkg6768To upgrade or downgrade a package to a specific version:6970	go get example.com/pkg@v1.2.37172To remove a dependency on a module and downgrade modules that require it:7374	go get example.com/mod@none7576To upgrade the minimum required Go version to the latest released Go version:7778	go get go@latest7980To upgrade the Go toolchain to the latest patch release of the current Go toolchain:8182	go get toolchain@patch8384See https://go.dev/ref/mod#go-get for details.8586In earlier versions of Go, 'go get' was used to build and install packages.87Now, 'go get' is dedicated to adjusting dependencies in go.mod. 'go install'88may be used to build and install commands instead. When a version is specified,89'go install' runs in module-aware mode and ignores the go.mod file in the90current directory. For example:9192	go install example.com/pkg@v1.2.393	go install example.com/pkg@latest9495See 'go help install' or https://go.dev/ref/mod#go-install for details.9697'go get' accepts the following flags.9899The -t flag instructs get to consider modules needed to build tests of100packages specified on the command line.101102The -u flag instructs get to update modules providing dependencies103of packages named on the command line to use newer minor or patch104releases when available.105106The -u=patch flag (not -u patch) also instructs get to update dependencies,107but changes the default to select patch releases.108109When the -t and -u flags are used together, get will update110test dependencies as well.111112The -tool flag instructs go to add a matching tool line to go.mod for each113listed package. If -tool is used with @none, the line will be removed.114See 'go help tool' for more information.115116The -x flag prints commands as they are executed. This is useful for117debugging version control commands when a module is downloaded directly118from a repository.119120For more about build flags, see 'go help build'.121122For more about modules, see https://go.dev/ref/mod.123124For more about using 'go get' to update the minimum Go version and125suggested Go toolchain, see https://go.dev/doc/toolchain.126127For more about specifying packages, see 'go help packages'.128129See also: go build, go install, go clean, go mod.130	`,131}132133var HelpVCS = &base.Command{134	UsageLine: "vcs",135	Short:     "controlling version control with GOVCS",136	Long: `137The go command can run version control commands like git138to download imported code. This functionality is critical to the decentralized139Go package ecosystem, in which code can be imported from any server,140but it is also a potential security problem, if a malicious server finds a141way to cause the invoked version control command to run unintended code.142143To balance the functionality and security concerns, the go command144by default will only use git and hg to download code from public servers.145But it will use any known version control system (fossil, git, hg, svn)146to download code from private servers, defined as those hosting packages147matching the GOPRIVATE variable (see 'go help private'). The rationale behind148allowing only Git and Mercurial is that these two systems have had the most149attention to issues of being run as clients of untrusted servers. In contrast,150Bazaar, Fossil, and Subversion have primarily been used in trusted,151authenticated environments and are not as well scrutinized as attack surfaces.152153The version control command restrictions only apply when using direct version154control access to download code. When downloading modules from a proxy,155the go command uses the proxy protocol instead, which is always permitted.156By default, the go command uses the Go module mirror (proxy.golang.org)157for public packages and only falls back to version control for private158packages or when the mirror refuses to serve a public package (typically for159legal reasons). Therefore, clients can still access public code served from160Bazaar, Fossil, or Subversion repositories by default, because those downloads161use the Go module mirror, which takes on the security risk of running the162version control commands using a custom sandbox.163164The GOVCS variable can be used to change the allowed version control systems165for specific packages (identified by a module or import path).166The GOVCS variable applies when building package in both module-aware mode167and GOPATH mode. When using modules, the patterns match against the module path.168When using GOPATH, the patterns match against the import path corresponding to169the root of the version control repository.170171The general form of the GOVCS setting is a comma-separated list of172pattern:vcslist rules. The pattern is a glob pattern that must match173one or more leading elements of the module or import path. The vcslist174is a pipe-separated list of allowed version control commands, or "all"175to allow use of any known command, or "off" to disallow all commands.176Note that if a module matches a pattern with vcslist "off", it may still be177downloaded if the origin server uses the "mod" scheme, which instructs the178go command to download the module using the GOPROXY protocol.179The earliest matching pattern in the list applies, even if later patterns180might also match.181182For example, consider:183184	GOVCS=github.com:git,evil.com:off,*:git|hg185186With this setting, code with a module or import path beginning with187github.com/ can only use git; paths on evil.com cannot use any version188control command, and all other paths (* matches everything) can use189only git or hg.190191The special patterns "public" and "private" match public and private192module or import paths. A path is private if it matches the GOPRIVATE193variable; otherwise it is public.194195If no rules in the GOVCS variable match a particular module or import path,196the 'go get' command applies its default rule, which can now be summarized197in GOVCS notation as 'public:git|hg,private:all'.198199To allow unfettered use of any version control system for any package, use:200201	GOVCS=*:all202203To disable all use of version control, use:204205	GOVCS=*:off206207The 'go env -w' command (see 'go help env') can be used to set the GOVCS208variable for future go command invocations.209`,210}211212var (213	getD        dFlag214	getF        = CmdGet.Flag.Bool("f", false, "")215	getFix      = CmdGet.Flag.Bool("fix", false, "")216	getM        = CmdGet.Flag.Bool("m", false, "")217	getT        = CmdGet.Flag.Bool("t", false, "")218	getU        upgradeFlag219	getTool     = CmdGet.Flag.Bool("tool", false, "")220	getInsecure = CmdGet.Flag.Bool("insecure", false, "")221)222223// upgradeFlag is a custom flag.Value for -u.224type upgradeFlag struct {225	rawVersion string226	version    string227}228229func (*upgradeFlag) IsBoolFlag() bool { return true } // allow -u230231func (v *upgradeFlag) Set(s string) error {232	if s == "false" {233		v.version = ""234		v.rawVersion = ""235	} else if s == "true" {236		v.version = "upgrade"237		v.rawVersion = ""238	} else {239		v.version = s240		v.rawVersion = s241	}242	return nil243}244245func (v *upgradeFlag) String() string { return "" }246247// dFlag is a custom flag.Value for the deprecated -d flag248// which will be used to provide warnings or errors if -d249// is provided.250type dFlag struct {251	value bool252	set   bool253}254255func (v *dFlag) IsBoolFlag() bool { return true }256257func (v *dFlag) Set(s string) error {258	v.set = true259	value, err := strconv.ParseBool(s)260	if err != nil {261		err = errors.New("parse error")262	}263	v.value = value264	return err265}266267func (b *dFlag) String() string { return "" }268269func init() {270	work.AddBuildFlags(CmdGet, work.OmitModFlag)271	CmdGet.Run = runGet // break init loop272	CmdGet.Flag.Var(&getD, "d", "")273	CmdGet.Flag.Var(&getU, "u", "")274}275276func runGet(ctx context.Context, cmd *base.Command, args []string) {277	moduleLoader := modload.NewLoader()278	switch getU.version {279	case "", "upgrade", "patch":280		// ok281	default:282		base.Fatalf("go: unknown upgrade flag -u=%s", getU.rawVersion)283	}284	if getD.set {285		if !getD.value {286			base.Fatalf("go: -d flag may not be set to false")287		}288		fmt.Fprintf(os.Stderr, "go: -d flag is deprecated. -d=true is a no-op\n")289	}290	if *getF {291		fmt.Fprintf(os.Stderr, "go: -f flag is a no-op\n")292	}293	if *getFix {294		fmt.Fprintf(os.Stderr, "go: -fix flag is a no-op\n")295	}296	if *getM {297		base.Fatalf("go: -m flag is no longer supported")298	}299	if *getInsecure {300		base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead")301	}302303	moduleLoader.ForceUseModules = true304305	// Do not allow any updating of go.mod until we've applied306	// all the requested changes and checked that the result matches307	// what was requested.308	modload.ExplicitWriteGoMod = true309310	// Allow looking up modules for import paths when outside of a module.311	// 'go get' is expected to do this, unlike other commands.312	moduleLoader.AllowMissingModuleImports()313314	// 'go get' no longer builds or installs packages, so there's nothing to do315	// if there's no go.mod file.316	// TODO(#40775): make modload.Init return ErrNoModRoot instead of exiting.317	// We could handle that here by printing a different message.318	modload.Init(moduleLoader)319	if !moduleLoader.HasModRoot() {320		base.Fatalf("go: go.mod file not found in current directory or any parent directory.\n" +321			"\t'go get' is no longer supported outside a module.\n" +322			"\tTo build and install a command, use 'go install' with a version,\n" +323			"\tlike 'go install example.com/cmd@latest'\n" +324			"\tFor more information, see https://go.dev/doc/go-get-install-deprecation\n" +325			"\tor run 'go help get' or 'go help install'.")326	}327328	dropToolchain, queries := parseArgs(moduleLoader, ctx, args)329	opts := modload.WriteOpts{330		DropToolchain: dropToolchain,331	}332	for _, q := range queries {333		if q.pattern == "toolchain" {334			opts.ExplicitToolchain = true335		}336	}337338	r := newResolver(moduleLoader, ctx, queries)339	r.performLocalQueries(moduleLoader, ctx)340	r.performPathQueries(moduleLoader, ctx)341	r.performToolQueries(moduleLoader, ctx)342	r.performWorkQueries(moduleLoader, ctx)343344	for {345		r.performWildcardQueries(moduleLoader, ctx)346		r.performPatternAllQueries(moduleLoader, ctx)347348		if changed := r.resolveQueries(moduleLoader, ctx, queries); changed {349			// 'go get' arguments can be (and often are) package patterns rather than350			// (just) modules. A package can be provided by any module with a prefix351			// of its import path, and a wildcard can even match packages in modules352			// with totally different paths. Because of these effects, and because any353			// change to the selected version of a module can bring in entirely new354			// module paths as dependencies, we need to reissue queries whenever we355			// change the build list.356			//357			// The result of any version query for a given module — even "upgrade" or358			// "patch" — is always relative to the build list at the start of359			// the 'go get' command, not an intermediate state, and is therefore360			// deterministic and therefore cacheable, and the constraints on the361			// selected version of each module can only narrow as we iterate.362			//363			// "all" is functionally very similar to a wildcard pattern. The set of364			// packages imported by the main module does not change, and the query365			// result for the module containing each such package also does not change366			// (it is always relative to the initial build list, before applying367			// queries). So the only way that the result of an "all" query can change368			// is if some matching package moves from one module in the build list369			// to another, which should not happen very often.370			continue371		}372373		// When we load imports, we detect the following conditions:374		//375		// - missing transitive dependencies that need to be resolved from outside the376		//   current build list (note that these may add new matches for existing377		//   pattern queries!)378		//379		// - transitive dependencies that didn't match any other query,380		//   but need to be upgraded due to the -u flag381		//382		// - ambiguous import errors.383		//   TODO(#27899): Try to resolve ambiguous import errors automatically.384		upgrades := r.findAndUpgradeImports(moduleLoader, ctx, queries)385		if changed := r.applyUpgrades(moduleLoader, ctx, upgrades); changed {386			continue387		}388389		r.findMissingWildcards(moduleLoader, ctx)390		if changed := r.resolveQueries(moduleLoader, ctx, r.wildcardQueries); changed {391			continue392		}393394		break395	}396397	r.checkWildcardVersions(moduleLoader, ctx)398399	var pkgPatterns []string400	for _, q := range queries {401		if q.matchesPackages {402			pkgPatterns = append(pkgPatterns, q.pattern)403		}404	}405406	if *getTool {407		updateTools(moduleLoader, ctx, r, queries, &opts)408	}409410	// If a workspace applies, checkPackageProblems will switch to the workspace411	// using modload.EnterWorkspace when doing the final load, and then switch back.412	r.checkPackageProblems(moduleLoader, ctx, pkgPatterns)413414	// Everything succeeded. Update go.mod.415	oldReqs := reqsFromGoMod(modload.ModFile(moduleLoader))416417	if err := modload.WriteGoMod(moduleLoader, ctx, opts); err != nil {418		// A TooNewError can happen for 'go get go@newversion'419		// when all the required modules are old enough420		// but the command line is not.421		// TODO(bcmills): modload.EditBuildList should catch this instead,422		// and then this can be changed to base.Fatal(err).423		toolchain.SwitchOrFatal(moduleLoader, ctx, err)424	}425426	newReqs := reqsFromGoMod(modload.ModFile(moduleLoader))427	r.reportChanges(oldReqs, newReqs)428429	if gowork := moduleLoader.FindGoWork(base.Cwd()); gowork != "" {430		wf, err := modload.ReadWorkFile(gowork)431		if err == nil && modload.UpdateWorkGoVersion(wf, moduleLoader.MainModules.GoVersion(moduleLoader)) {432			modload.WriteWorkFile(gowork, wf)433		}434	}435}436437func updateTools(ld *modload.Loader, ctx context.Context, r *resolver, queries []*query, opts *modload.WriteOpts) {438	pkgOpts := modload.PackageOpts{439		VendorModulesInGOROOTSrc: true,440		LoadTests:                *getT,441		ResolveMissingImports:    false,442		AllowErrors:              true,443		SilenceNoGoErrors:        true,444	}445	patterns := []string{}446	for _, q := range queries {447		if search.IsMetaPackage(q.pattern) || q.pattern == "toolchain" {448			base.Fatalf("go: go get -tool does not work with \"%s\".", q.pattern)449		}450		patterns = append(patterns, q.pattern)451	}452453	matches, _ := modload.LoadPackages(ld, ctx, pkgOpts, patterns...)454	for i, m := range matches {455		if queries[i].version == "none" {456			opts.DropTools = append(opts.DropTools, m.Pkgs...)457		} else {458			opts.AddTools = append(opts.AddTools, m.Pkgs...)459		}460	}461462	mg, err := modload.LoadModGraph(ld, ctx, "")463	if err != nil {464		toolchain.SwitchOrFatal(ld, ctx, err)465	}466	r.buildList = mg.BuildList()467	r.buildListVersion = make(map[string]string, len(r.buildList))468	for _, m := range r.buildList {469		r.buildListVersion[m.Path] = m.Version470	}471}472473// parseArgs parses command-line arguments and reports errors.474//475// The command-line arguments are of the form path@version or simply path, with476// implicit @upgrade. path@none is "downgrade away".477func parseArgs(ld *modload.Loader, ctx context.Context, rawArgs []string) (dropToolchain bool, queries []*query) {478	defer base.ExitIfErrors()479480	for _, arg := range search.CleanPatterns(rawArgs) {481		q, err := newQuery(ld, arg)482		if err != nil {483			base.Error(err)484			continue485		}486487		if q.version == "none" {488			switch q.pattern {489			case "go":490				base.Errorf("go: cannot use go@none")491				continue492			case "toolchain":493				dropToolchain = true494				continue495			}496		}497498		// If there were no arguments, CleanPatterns returns ".". Set the raw499		// string back to "" for better errors.500		if len(rawArgs) == 0 {501			q.raw = ""502		}503504		// Guard against 'go get x.go', a common mistake.505		// Note that package and module paths may end with '.go', so only print an error506		// if the argument has no version and either has no slash or refers to an existing file.507		if strings.HasSuffix(q.raw, ".go") && q.rawVersion == "" {508			if !strings.Contains(q.raw, "/") {509				base.Errorf("go: %s: arguments must be package or module paths", q.raw)510				continue511			}512			if fi, err := os.Stat(q.raw); err == nil && !fi.IsDir() {513				base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", q.raw)514				continue515			}516		}517518		queries = append(queries, q)519	}520521	return dropToolchain, queries522}523524type resolver struct {525	localQueries      []*query // queries for absolute or relative paths526	pathQueries       []*query // package path literal queries in original order527	wildcardQueries   []*query // path wildcard queries in original order528	patternAllQueries []*query // queries with the pattern "all"529	workQueries       []*query // queries with the pattern "work"530	toolQueries       []*query // queries with the pattern "tool"531532	// Indexed "none" queries. These are also included in the slices above;533	// they are indexed here to speed up noneForPath.534	nonesByPath   map[string]*query // path-literal "@none" queries indexed by path535	wildcardNones []*query          // wildcard "@none" queries536537	// resolvedVersion maps each module path to the version of that module that538	// must be selected in the final build list, along with the first query539	// that resolved the module to that version (the “reason”).540	resolvedVersion map[string]versionReason541542	buildList        []module.Version543	buildListVersion map[string]string // index of buildList (module path → version)544545	initialVersion map[string]string // index of the initial build list at the start of 'go get'546547	missing []pathSet // candidates for missing transitive dependencies548549	work *par.Queue550551	matchInModuleCache par.ErrCache[matchInModuleKey, []string]552553	// workspace is used to check whether, in workspace mode, any of the workspace554	// modules would contain a package.555	workspace *workspace556}557558type versionReason struct {559	version string560	reason  *query561}562563type matchInModuleKey struct {564	pattern string565	m       module.Version566}567568func newResolver(ld *modload.Loader, ctx context.Context, queries []*query) *resolver {569	// LoadModGraph also sets modload.Target, which is needed by various resolver570	// methods.571	mg, err := modload.LoadModGraph(ld, ctx, "")572	if err != nil {573		toolchain.SwitchOrFatal(ld, ctx, err)574	}575576	buildList := mg.BuildList()577	initialVersion := make(map[string]string, len(buildList))578	for _, m := range buildList {579		initialVersion[m.Path] = m.Version580	}581582	r := &resolver{583		work:             par.NewQueue(runtime.GOMAXPROCS(0)),584		resolvedVersion:  map[string]versionReason{},585		buildList:        buildList,586		buildListVersion: initialVersion,587		initialVersion:   initialVersion,588		nonesByPath:      map[string]*query{},589		workspace:        loadWorkspace(ld.FindGoWork(base.Cwd())),590	}591592	for _, q := range queries {593		if q.pattern == "all" {594			r.patternAllQueries = append(r.patternAllQueries, q)595		} else if q.pattern == "work" {596			r.workQueries = append(r.workQueries, q)597		} else if q.pattern == "tool" {598			r.toolQueries = append(r.toolQueries, q)599		} else if q.patternIsLocal {600			r.localQueries = append(r.localQueries, q)601		} else if q.isWildcard() {602			r.wildcardQueries = append(r.wildcardQueries, q)603		} else {604			r.pathQueries = append(r.pathQueries, q)605		}606607		if q.version == "none" {608			// Index "none" queries to make noneForPath more efficient.609			if q.isWildcard() {610				r.wildcardNones = append(r.wildcardNones, q)611			} else {612				// All "<path>@none" queries for the same path are identical; we only613				// need to index one copy.614				r.nonesByPath[q.pattern] = q615			}616		}617	}618619	return r620}621622// initialSelected returns the version of the module with the given path that623// was selected at the start of this 'go get' invocation.624func (r *resolver) initialSelected(mPath string) (version string) {625	v, ok := r.initialVersion[mPath]626	if !ok {627		return "none"628	}629	return v630}631632// selected returns the version of the module with the given path that is633// selected in the resolver's current build list.634func (r *resolver) selected(mPath string) (version string) {635	v, ok := r.buildListVersion[mPath]636	if !ok {637		return "none"638	}639	return v640}641642// noneForPath returns a "none" query matching the given module path,643// or found == false if no such query exists.644func (r *resolver) noneForPath(mPath string) (nq *query, found bool) {645	if nq = r.nonesByPath[mPath]; nq != nil {646		return nq, true647	}648	for _, nq := range r.wildcardNones {649		if nq.matchesPath(mPath) {650			return nq, true651		}652	}653	return nil, false654}655656// queryModule wraps modload.Query, substituting r.checkAllowedOr to decide657// allowed versions.658func (r *resolver) queryModule(ld *modload.Loader, ctx context.Context, mPath, query string, selected func(string) string) (module.Version, error) {659	current := r.initialSelected(mPath)660	rev, err := modload.Query(ld, ctx, mPath, query, current, r.checkAllowedOr(ld, query, selected))661	if err != nil {662		return module.Version{}, err663	}664	return module.Version{Path: mPath, Version: rev.Version}, nil665}666667// queryPackages wraps modload.QueryPackage, substituting r.checkAllowedOr to668// decide allowed versions.669func (r *resolver) queryPackages(ld *modload.Loader, ctx context.Context, pattern, query string, selected func(string) string) (pkgMods []module.Version, err error) {670	results, err := modload.QueryPackages(ld, ctx, pattern, query, selected, r.checkAllowedOr(ld, query, selected))671	if len(results) > 0 {672		pkgMods = make([]module.Version, 0, len(results))673		for _, qr := range results {674			pkgMods = append(pkgMods, qr.Mod)675		}676	}677	return pkgMods, err678}679680// queryPattern wraps modload.QueryPattern, substituting r.checkAllowedOr to681// decide allowed versions.682func (r *resolver) queryPattern(ld *modload.Loader, ctx context.Context, pattern, query string, selected func(string) string) (pkgMods []module.Version, mod module.Version, err error) {683	results, modOnly, err := modload.QueryPattern(ld, ctx, pattern, query, selected, r.checkAllowedOr(ld, query, selected))684	if len(results) > 0 {685		pkgMods = make([]module.Version, 0, len(results))686		for _, qr := range results {687			pkgMods = append(pkgMods, qr.Mod)688		}689	}690	if modOnly != nil {691		mod = modOnly.Mod692	}693	return pkgMods, mod, err694}695696// checkAllowedOr is like modload.CheckAllowed, but it always allows the requested697// and current versions (even if they are retracted or otherwise excluded).698func (r *resolver) checkAllowedOr(s *modload.Loader, requested string, selected func(string) string) modload.AllowedFunc {699	return func(ctx context.Context, m module.Version) error {700		if m.Version == requested {701			return s.CheckExclusions(ctx, m)702		}703		if (requested == "upgrade" || requested == "patch") && m.Version == selected(m.Path) {704			return nil705		}706		return s.CheckAllowed(ctx, m)707	}708}709710// matchInModule is a caching wrapper around modload.MatchInModule.711func (r *resolver) matchInModule(ld *modload.Loader, ctx context.Context, pattern string, m module.Version) (packages []string, err error) {712	return r.matchInModuleCache.Do(matchInModuleKey{pattern, m}, func() ([]string, error) {713		match := modload.MatchInModule(ld, ctx, pattern, m, imports.AnyTags())714		if len(match.Errs) > 0 {715			return match.Pkgs, match.Errs[0]716		}717		return match.Pkgs, nil718	})719}720721// queryNone adds a candidate set to q for each module matching q.pattern.722// Each candidate set has only one possible module version: the matched723// module at version "none".724//725// We interpret arguments to 'go get' as packages first, and fall back to726// modules second. However, no module exists at version "none", and therefore no727// package exists at that version either: we know that the argument cannot match728// any packages, and thus it must match modules instead.729func (r *resolver) queryNone(ld *modload.Loader, ctx context.Context, q *query) {730	if search.IsMetaPackage(q.pattern) {731		panic(fmt.Sprintf("internal error: queryNone called with pattern %q", q.pattern))732	}733734	if !q.isWildcard() {735		q.pathOnce(q.pattern, func() pathSet {736			hasModRoot := ld.HasModRoot()737			if hasModRoot && ld.MainModules.Contains(q.pattern) {738				v := module.Version{Path: q.pattern}739				// The user has explicitly requested to downgrade their own module to740				// version "none". This is not an entirely unreasonable request: it741				// could plausibly mean “downgrade away everything that depends on any742				// explicit version of the main module”, or “downgrade away the743				// package with the same path as the main module, found in a module744				// with a prefix of the main module's path”.745				//746				// However, neither of those behaviors would be consistent with the747				// plain meaning of the query. To try to reduce confusion, reject the748				// query explicitly.749				return errSet(&modload.QueryMatchesMainModulesError{750					MainModules:     []module.Version{v},751					Pattern:         q.pattern,752					Query:           q.version,753					PatternIsModule: ld.MainModules.Contains(q.pattern),754				})755			}756757			return pathSet{mod: module.Version{Path: q.pattern, Version: "none"}}758		})759	}760761	for _, curM := range r.buildList {762		if !q.matchesPath(curM.Path) {763			continue764		}765		q.pathOnce(curM.Path, func() pathSet {766			if ld.HasModRoot() && curM.Version == "" && ld.MainModules.Contains(curM.Path) {767				return errSet(&modload.QueryMatchesMainModulesError{768					MainModules:     []module.Version{curM},769					Pattern:         q.pattern,770					Query:           q.version,771					PatternIsModule: ld.MainModules.Contains(q.pattern),772				})773			}774			return pathSet{mod: module.Version{Path: curM.Path, Version: "none"}}775		})776	}777}778779func (r *resolver) performLocalQueries(ld *modload.Loader, ctx context.Context) {780	for _, q := range r.localQueries {781		q.pathOnce(q.pattern, func() pathSet {782			absDetail := ""783			if !filepath.IsAbs(q.pattern) {784				if absPath, err := filepath.Abs(q.pattern); err == nil {785					absDetail = fmt.Sprintf(" (%s)", absPath)786				}787			}788789			// Absolute paths like C:\foo and relative paths like ../foo... are790			// restricted to matching packages in the main module.791			pkgPattern, mainModule := ld.MainModules.DirImportPath(ld, ctx, q.pattern)792			if pkgPattern == "." {793				ld.MustHaveModRoot()794				versions := ld.MainModules.Versions()795				modRoots := make([]string, 0, len(versions))796				for _, m := range versions {797					modRoots = append(modRoots, ld.MainModules.ModRoot(m))798				}799				var plural string800				if len(modRoots) != 1 {801					plural = "s"802				}803				return errSet(fmt.Errorf("%s%s is not within module%s rooted at %s", q.pattern, absDetail, plural, strings.Join(modRoots, ", ")))804			}805806			match := modload.MatchInModule(ld, ctx, pkgPattern, mainModule, imports.AnyTags())807			if len(match.Errs) > 0 {808				return pathSet{err: match.Errs[0]}809			}810811			if len(match.Pkgs) == 0 {812				if q.raw == "" || q.raw == "." {813					return errSet(fmt.Errorf("no package to get in current directory"))814				}815				if !q.isWildcard() {816					ld.MustHaveModRoot()817					return errSet(fmt.Errorf("%s%s is not a package in module rooted at %s", q.pattern, absDetail, ld.MainModules.ModRoot(mainModule)))818				}819				search.WarnUnmatched([]*search.Match{match})820				return pathSet{}821			}822823			return pathSet{pkgMods: []module.Version{mainModule}}824		})825	}826}827828// performWildcardQueries populates the candidates for each query whose pattern829// is a wildcard.830//831// The candidates for a given module path matching (or containing a package832// matching) a wildcard query depend only on the initial build list, but the set833// of modules may be expanded by other queries, so wildcard queries need to be834// re-evaluated whenever a potentially-matching module path is added to the835// build list.836func (r *resolver) performWildcardQueries(ld *modload.Loader, ctx context.Context) {837	for _, q := range r.wildcardQueries {838		q := q839		r.work.Add(func() {840			if q.version == "none" {841				r.queryNone(ld, ctx, q)842			} else {843				r.queryWildcard(ld, ctx, q)844			}845		})846	}847	<-r.work.Idle()848}849850// queryWildcard adds a candidate set to q for each module for which:851//   - some version of the module is already in the build list, and852//   - that module exists at some version matching q.version, and853//   - either the module path itself matches q.pattern, or some package within854//     the module at q.version matches q.pattern.855func (r *resolver) queryWildcard(ld *modload.Loader, ctx context.Context, q *query) {856	// For wildcard patterns, modload.QueryPattern only identifies modules857	// matching the prefix of the path before the wildcard. However, the build858	// list may already contain other modules with matching packages, and we859	// should consider those modules to satisfy the query too.860	// We want to match any packages in existing dependencies, but we only want to861	// resolve new dependencies if nothing else turns up.862	for _, curM := range r.buildList {863		if !q.canMatchInModule(curM.Path) {864			continue865		}866		q.pathOnce(curM.Path, func() pathSet {867			if _, hit := r.noneForPath(curM.Path); hit {868				// This module is being removed, so it will no longer be in the build list869				// (and thus will no longer match the pattern).870				return pathSet{}871			}872873			if ld.MainModules.Contains(curM.Path) && !versionOkForMainModule(q.version) {874				if q.matchesPath(curM.Path) {875					return errSet(&modload.QueryMatchesMainModulesError{876						MainModules:     []module.Version{curM},877						Pattern:         q.pattern,878						Query:           q.version,879						PatternIsModule: ld.MainModules.Contains(q.pattern),880					})881				}882883				packages, err := r.matchInModule(ld, ctx, q.pattern, curM)884				if err != nil {885					return errSet(err)886				}887				if len(packages) > 0 {888					return errSet(&modload.QueryMatchesPackagesInMainModuleError{889						Pattern:  q.pattern,890						Query:    q.version,891						Packages: packages,892					})893				}894895				return r.tryWildcard(ld, ctx, q, curM)896			}897898			m, err := r.queryModule(ld, ctx, curM.Path, q.version, r.initialSelected)899			if err != nil {900				if !isNoSuchModuleVersion(err) {901					// We can't tell whether a matching version exists.902					return errSet(err)903				}904				// There is no version of curM.Path matching the query.905906				// We haven't checked whether curM contains any matching packages at its907				// currently-selected version, or whether curM.Path itself matches q. If908				// either of those conditions holds, *and* no other query changes the909				// selected version of curM, then we will fail in checkWildcardVersions.910				// (This could be an error, but it's too soon to tell.)911				//912				// However, even then the transitive requirements of some other query913				// may downgrade this module out of the build list entirely, in which914				// case the pattern will no longer include it and it won't be an error.915				//916				// Either way, punt on the query rather than erroring out just yet.917				return pathSet{}918			}919920			return r.tryWildcard(ld, ctx, q, m)921		})922	}923924	// Even if no modules matched, we shouldn't query for a new module to provide925	// the pattern yet: some other query may yet induce a new requirement that926	// will match the wildcard. Instead, we'll check in findMissingWildcards.927}928929// tryWildcard returns a pathSet for module m matching query q.930// If m does not actually match q, tryWildcard returns an empty pathSet.931func (r *resolver) tryWildcard(ld *modload.Loader, ctx context.Context, q *query, m module.Version) pathSet {932	mMatches := q.matchesPath(m.Path)933	packages, err := r.matchInModule(ld, ctx, q.pattern, m)934	if err != nil {935		return errSet(err)936	}937	if len(packages) > 0 {938		return pathSet{pkgMods: []module.Version{m}}939	}940	if mMatches {941		return pathSet{mod: m}942	}943	return pathSet{}944}945946// findMissingWildcards adds a candidate set for each query in r.wildcardQueries947// that has not yet resolved to any version containing packages.948func (r *resolver) findMissingWildcards(ld *modload.Loader, ctx context.Context) {949	for _, q := range r.wildcardQueries {950		if q.version == "none" || q.matchesPackages {951			continue // q is not “missing”952		}953		r.work.Add(func() {954			q.pathOnce(q.pattern, func() pathSet {955				pkgMods, mod, err := r.queryPattern(ld, ctx, q.pattern, q.version, r.initialSelected)956				if err != nil {957					if isNoSuchPackageVersion(err) && len(q.resolved) > 0 {958						// q already resolved one or more modules but matches no packages.959						// That's ok: this pattern is just a module pattern, and we don't960						// need to add any more modules to satisfy it.961						return pathSet{}962					}963					return errSet(err)964				}965966				return pathSet{pkgMods: pkgMods, mod: mod}967			})968		})969	}970	<-r.work.Idle()971}972973// checkWildcardVersions reports an error if any module in the build list has a974// path (or contains a package) matching a query with a wildcard pattern, but975// has a selected version that does *not* match the query.976func (r *resolver) checkWildcardVersions(ld *modload.Loader, ctx context.Context) {977	defer base.ExitIfErrors()978979	for _, q := range r.wildcardQueries {980		for _, curM := range r.buildList {981			if !q.canMatchInModule(curM.Path) {982				continue983			}984			if !q.matchesPath(curM.Path) {985				packages, err := r.matchInModule(ld, ctx, q.pattern, curM)986				if len(packages) == 0 {987					if err != nil {988						reportError(q, err)989					}990					continue // curM is not relevant to q.991				}992			}993994			rev, err := r.queryModule(ld, ctx, curM.Path, q.version, r.initialSelected)995			if err != nil {996				reportError(q, err)997				continue998			}999			if rev.Version == curM.Version {1000				continue // curM already matches q.1001			}10021003			if !q.matchesPath(curM.Path) {1004				m := module.Version{Path: curM.Path, Version: rev.Version}1005				packages, err := r.matchInModule(ld, ctx, q.pattern, m)1006				if err != nil {1007					reportError(q, err)1008					continue1009				}1010				if len(packages) == 0 {1011					// curM at its original version contains a path matching q.pattern,1012					// but at rev.Version it does not, so (somewhat paradoxically) if1013					// we changed the version of curM it would no longer match the query.1014					var version any = m1015					if rev.Version != q.version {1016						version = fmt.Sprintf("%s@%s (%s)", m.Path, q.version, m.Version)1017					}1018					reportError(q, fmt.Errorf("%v matches packages in %v but not %v: specify a different version for module %s", q, curM, version, m.Path))1019					continue1020				}1021			}10221023			// Since queryModule succeeded and either curM or one of the packages it1024			// contains matches q.pattern, we should have either selected the version1025			// of curM matching q, or reported a conflict error (and exited).1026			// If we're still here and the version doesn't match,1027			// something has gone very wrong.1028			reportError(q, fmt.Errorf("internal error: selected %v instead of %v", curM, rev.Version))1029		}1030	}1031}10321033// performPathQueries populates the candidates for each query whose pattern is1034// a path literal.1035//1036// The candidate packages and modules for path literals depend only on the1037// initial build list, not the current build list, so we only need to query path1038// literals once.1039func (r *resolver) performPathQueries(ld *modload.Loader, ctx context.Context) {1040	for _, q := range r.pathQueries {1041		q := q1042		r.work.Add(func() {1043			if q.version == "none" {1044				r.queryNone(ld, ctx, q)1045			} else {1046				r.queryPath(ld, ctx, q)1047			}1048		})1049	}1050	<-r.work.Idle()1051}10521053// queryPath adds a candidate set to q for the package with path q.pattern.1054// The candidate set consists of all modules that could provide q.pattern1055// and have a version matching q, plus (if it exists) the module whose path1056// is itself q.pattern (at a matching version).1057func (r *resolver) queryPath(ld *modload.Loader, ctx context.Context, q *query) {1058	q.pathOnce(q.pattern, func() pathSet {1059		if search.IsMetaPackage(q.pattern) || q.isWildcard() {1060			panic(fmt.Sprintf("internal error: queryPath called with pattern %q", q.pattern))1061		}1062		if q.version == "none" {1063			panic(`internal error: queryPath called with version "none"`)1064		}10651066		if search.IsStandardImportPath(q.pattern) {1067			stdOnly := module.Version{}1068			packages, _ := r.matchInModule(ld, ctx, q.pattern, stdOnly)1069			if len(packages) > 0 {1070				if q.rawVersion != "" {1071					return errSet(fmt.Errorf("can't request explicit version %q of standard library package %s", q.version, q.pattern))1072				}10731074				q.matchesPackages = true1075				return pathSet{} // No module needed for standard library.1076			}1077		}10781079		pkgMods, mod, err := r.queryPattern(ld, ctx, q.pattern, q.version, r.initialSelected)1080		if err != nil {1081			return errSet(err)1082		}1083		return pathSet{pkgMods: pkgMods, mod: mod}1084	})1085}10861087// performToolQueries populates the candidates for each query whose1088// pattern is "tool".1089func (r *resolver) performToolQueries(ld *modload.Loader, ctx context.Context) {1090	for _, q := range r.toolQueries {1091		for tool := range ld.MainModules.Tools() {1092			q.pathOnce(tool, func() pathSet {1093				pkgMods, err := r.queryPackages(ld, ctx, tool, q.version, r.initialSelected)1094				return pathSet{pkgMods: pkgMods, err: err}1095			})1096		}1097	}1098}10991100// performWorkQueries populates the candidates for each query whose pattern is "work".1101// The candidate module to resolve the work pattern is exactly the single main module.1102func (r *resolver) performWorkQueries(ld *modload.Loader, ctx context.Context) {1103	for _, q := range r.workQueries {1104		q.pathOnce(q.pattern, func() pathSet {1105			// TODO(matloob): Maybe export MainModules.mustGetSingleMainModule and call that.1106			// There are a few other places outside the modload package where we expect1107			// a single main module.1108			if len(ld.MainModules.Versions()) != 1 {1109				panic("internal error: number of main modules is not exactly one in resolution phase of go get")1110			}1111			mainModule := ld.MainModules.Versions()[0]11121113			// We know what the result is going to be, assuming the main module is not1114			// empty, (it's the main module itself) but first check to see that there1115			// are packages in the main module, so that if there aren't any, we can1116			// return the expected warning that the pattern matched no packages.1117			match := modload.MatchInModule(ld, ctx, q.pattern, mainModule, imports.AnyTags())1118			if len(match.Errs) > 0 {1119				return pathSet{err: match.Errs[0]}1120			}1121			if len(match.Pkgs) == 0 {1122				search.WarnUnmatched([]*search.Match{match})1123				return pathSet{} // There are no packages in the main module, so the main module isn't needed to resolve them.1124			}11251126			return pathSet{pkgMods: []module.Version{mainModule}}1127		})1128	}1129}11301131// performPatternAllQueries populates the candidates for each query whose1132// pattern is "all".1133//1134// The candidate modules for a given package in "all" depend only on the initial1135// build list, but we cannot follow the dependencies of a given package until we1136// know which candidate is selected — and that selection may depend on the1137// results of other queries. We need to re-evaluate the "all" queries whenever1138// the module for one or more packages in "all" are resolved.1139func (r *resolver) performPatternAllQueries(ld *modload.Loader, ctx context.Context) {1140	if len(r.patternAllQueries) == 0 {1141		return1142	}11431144	findPackage := func(ctx context.Context, path string, m module.Version) (versionOk bool) {1145		versionOk = true1146		for _, q := range r.patternAllQueries {1147			q.pathOnce(path, func() pathSet {1148				pkgMods, err := r.queryPackages(ld, ctx, path, q.version, r.initialSelected)1149				if len(pkgMods) != 1 || pkgMods[0] != m {1150					// There are candidates other than m for the given path, so we can't1151					// be certain that m will actually be the module selected to provide1152					// the package. Don't load its dependencies just yet, because they1153					// might no longer be dependencies after we resolve the correct1154					// version.1155					versionOk = false1156				}1157				return pathSet{pkgMods: pkgMods, err: err}1158			})1159		}1160		return versionOk1161	}11621163	r.loadPackages(ld, ctx, []string{"all"}, findPackage)11641165	// Since we built up the candidate lists concurrently, they may be in a1166	// nondeterministic order. We want 'go get' to be fully deterministic,1167	// including in which errors it chooses to report, so sort the candidates1168	// into a deterministic-but-arbitrary order.1169	for _, q := range r.patternAllQueries {1170		sort.Slice(q.candidates, func(i, j int) bool {1171			return q.candidates[i].path < q.candidates[j].path1172		})1173	}1174}11751176// findAndUpgradeImports returns a pathSet for each package that is not yet1177// in the build list but is transitively imported by the packages matching the1178// given queries (which must already have been resolved).1179//1180// If the getU flag ("-u") is set, findAndUpgradeImports also returns a1181// pathSet for each module that is not constrained by any other1182// command-line argument and has an available matching upgrade.1183func (r *resolver) findAndUpgradeImports(ld *modload.Loader, ctx context.Context, queries []*query) (upgrades []pathSet) {1184	patterns := make([]string, 0, len(queries))1185	for _, q := range queries {1186		if q.matchesPackages {1187			patterns = append(patterns, q.pattern)1188		}1189	}1190	if len(patterns) == 0 {1191		return nil1192	}11931194	// mu guards concurrent writes to upgrades, which will be sorted1195	// (to restore determinism) after loading.1196	var mu sync.Mutex11971198	findPackage := func(ctx context.Context, path string, m module.Version) (versionOk bool) {1199		version := "latest"1200		if m.Path != "" {1201			if getU.version == "" {1202				// The user did not request that we upgrade transitive dependencies.1203				return true1204			}1205			if _, ok := r.resolvedVersion[m.Path]; ok {1206				// We cannot upgrade m implicitly because its version is determined by1207				// an explicit pattern argument.1208				return true1209			}1210			version = getU.version1211		}12121213		// Unlike other queries, the "-u" flag upgrades relative to the build list1214		// after applying changes so far, not the initial build list.1215		// This is for two reasons:1216		//1217		// 	- The "-u" flag intentionally applies to transitive dependencies,1218		// 	  which may not be known or even resolved in advance of applying1219		// 	  other version changes.1220		//1221		// 	- The "-u" flag, unlike other arguments, does not cause version1222		// 	  conflicts with other queries. (The other query always wins.)12231224		pkgMods, err := r.queryPackages(ld, ctx, path, version, r.selected)1225		for _, u := range pkgMods {1226			if u == m {1227				// The selected package version is already upgraded appropriately; there1228				// is no need to change it.1229				return true1230			}1231		}12321233		if err != nil {1234			if isNoSuchPackageVersion(err) || (m.Path == "" && module.CheckPath(path) != nil) {1235				// We can't find the package because it doesn't — or can't — even exist1236				// in any module at the latest version. (Note that invalid module paths1237				// could in general exist due to replacements, so we at least need to1238				// run the query to check those.)1239				//1240				// There is no version change we can make to fix the package, so leave1241				// it unresolved. Either some other query (perhaps a wildcard matching a1242				// newly-added dependency for some other missing package) will fill in1243				// the gaps, or we will report an error (with a better import stack) in1244				// the final LoadPackages call.1245				return true1246			}1247		}12481249		mu.Lock()1250		upgrades = append(upgrades, pathSet{path: path, pkgMods: pkgMods, err: err})1251		mu.Unlock()1252		return false1253	}12541255	r.loadPackages(ld, ctx, patterns, findPackage)12561257	// Since we built up the candidate lists concurrently, they may be in a1258	// nondeterministic order. We want 'go get' to be fully deterministic,1259	// including in which errors it chooses to report, so sort the candidates1260	// into a deterministic-but-arbitrary order.1261	sort.Slice(upgrades, func(i, j int) bool {1262		return upgrades[i].path < upgrades[j].path1263	})1264	return upgrades1265}12661267// loadPackages loads the packages matching the given patterns, invoking the1268// findPackage function for each package that may require a change to the1269// build list.1270//1271// loadPackages invokes the findPackage function for each package loaded from a1272// module outside the main module. If the module or version that supplies that1273// package needs to be changed due to a query, findPackage may return false1274// and the imports of that package will not be loaded.1275//1276// loadPackages also invokes the findPackage function for each imported package1277// that is neither present in the standard library nor in any module in the1278// build list.1279func (r *resolver) loadPackages(ld *modload.Loader, ctx context.Context, patterns []string, findPackage func(ctx context.Context, path string, m module.Version) (versionOk bool)) {1280	opts := modload.PackageOpts{1281		Tags:                     imports.AnyTags(),1282		VendorModulesInGOROOTSrc: true,1283		LoadTests:                *getT,1284		AssumeRootsImported:      true, // After 'go get foo', imports of foo should build.1285		SilencePackageErrors:     true, // May be fixed by subsequent upgrades or downgrades.1286		Switcher:                 toolchain.NewSwitcher(ld),1287	}12881289	opts.AllowPackage = func(ctx context.Context, path string, m module.Version) error {1290		if m.Path == "" || m.Version == "" {1291			// Packages in the standard library and main modules are already at their1292			// latest (and only) available versions.1293			return nil1294		}1295		if ok := findPackage(ctx, path, m); !ok {1296			return errVersionChange1297		}1298		return nil1299	}13001301	_, pkgs := modload.LoadPackages(ld, ctx, opts, patterns...)1302	for _, pkgPath := range pkgs {1303		const (1304			parentPath  = ""1305			parentIsStd = false1306		)1307		_, _, err := modload.Lookup(ld, parentPath, parentIsStd, pkgPath)1308		if err == nil {1309			continue1310		}1311		if errors.Is(err, errVersionChange) {1312			// We already added candidates during loading.1313			continue1314		}1315		if r.workspace != nil && r.workspace.hasPackage(pkgPath) {1316			// Don't try to resolve imports that are in the resolver's associated workspace. (#73654)1317			continue1318		}13191320		if _, ok := errors.AsType[*modload.ImportMissingError](err); !ok {1321			if _, ok := errors.AsType[*modload.AmbiguousImportError](err); !ok {1322				// The package, which is a dependency of something we care about, has some1323				// problem that we can't resolve with a version change.1324				// Leave the error for the final LoadPackages call.1325				continue1326			}1327		}13281329		path := pkgPath1330		r.work.Add(func() {1331			findPackage(ctx, path, module.Version{})1332		})1333	}1334	<-r.work.Idle()1335}13361337// errVersionChange is a sentinel error indicating that a module's version needs1338// to be updated before its dependencies can be loaded.1339var errVersionChange = errors.New("version change needed")13401341// resolveQueries resolves candidate sets that are attached to the given1342// queries and/or needed to provide the given missing-package dependencies.1343//1344// resolveQueries starts by resolving one module version from each1345// unambiguous pathSet attached to the given queries.1346//1347// If no unambiguous query results in a change to the build list,1348// resolveQueries revisits the ambiguous query candidates and resolves them1349// arbitrarily in order to guarantee forward progress.1350//1351// If all pathSets are resolved without any changes to the build list,1352// resolveQueries returns with changed=false.1353func (r *resolver) resolveQueries(ld *modload.Loader, ctx context.Context, queries []*query) (changed bool) {1354	defer base.ExitIfErrors()13551356	// Note: this is O(N²) with the number of pathSets in the worst case.1357	//1358	// We could perhaps get it down to O(N) if we were to index the pathSets1359	// by module path, so that we only revisit a given pathSet when the1360	// version of some module in its containingPackage list has been determined.1361	//1362	// However, N tends to be small, and most candidate sets will include only one1363	// candidate module (so they will be resolved in the first iteration), so for1364	// now we'll stick to the simple O(N²) approach.13651366	resolved := 01367	for {1368		prevResolved := resolved13691370		// If we found modules that were too new, find the max of the required versions1371		// and then try to switch to a newer toolchain.1372		sw := toolchain.NewSwitcher(ld)1373		for _, q := range queries {1374			for _, cs := range q.candidates {1375				sw.Error(cs.err)1376			}1377		}1378		// Only switch if we need a newer toolchain.1379		// Otherwise leave the cs.err for reporting later.1380		if sw.NeedSwitch() {1381			sw.Switch(ctx)1382			// If NeedSwitch is true and Switch returns, Switch has failed to locate a newer toolchain.1383			// It printed the errors along with one more about not finding a good toolchain.1384			base.Exit()1385		}13861387		for _, q := range queries {1388			unresolved := q.candidates[:0]13891390			for _, cs := range q.candidates {1391				if cs.err != nil {1392					reportError(q, cs.err)1393					resolved++1394					continue1395				}13961397				filtered, isPackage, m, unique := r.disambiguate(ld, cs)1398				if !unique {1399					unresolved = append(unresolved, filtered)1400					continue1401				}14021403				if m.Path == "" {1404					// The query is not viable. Choose an arbitrary candidate from1405					// before filtering and “resolve” it to report a conflict.1406					isPackage, m = r.chooseArbitrarily(cs)1407				}1408				if isPackage {1409					q.matchesPackages = true1410				}1411				r.resolve(ld, q, m)1412				resolved++1413			}14141415			q.candidates = unresolved1416		}14171418		base.ExitIfErrors()1419		if resolved == prevResolved {1420			break // No unambiguous candidate remains.1421		}1422	}14231424	if resolved > 0 {1425		if changed = r.updateBuildList(ld, ctx, nil); changed {1426			// The build list has changed, so disregard any remaining ambiguous queries:1427			// they might now be determined by requirements in the build list, which we1428			// would prefer to use instead of arbitrary versions.1429			return true1430		}1431	}14321433	// The build list will be the same on the next iteration as it was on this1434	// iteration, so any ambiguous queries will remain so. In order to make1435	// progress, resolve them arbitrarily but deterministically.1436	//1437	// If that results in conflicting versions, the user can re-run 'go get'1438	// with additional explicit versions for the conflicting packages or1439	// modules.1440	resolvedArbitrarily := 01441	for _, q := range queries {1442		for _, cs := range q.candidates {1443			isPackage, m := r.chooseArbitrarily(cs)1444			if isPackage {1445				q.matchesPackages = true1446			}1447			r.resolve(ld, q, m)1448			resolvedArbitrarily++1449		}1450	}1451	if resolvedArbitrarily > 0 {1452		changed = r.updateBuildList(ld, ctx, nil)1453	}1454	return changed1455}14561457// applyUpgrades disambiguates candidate sets that are needed to upgrade (or1458// provide) transitive dependencies imported by previously-resolved packages.1459//1460// applyUpgrades modifies the build list by adding one module version from each1461// pathSet in upgrades, then downgrading (or further upgrading) those modules as1462// needed to maintain any already-resolved versions of other modules.1463// applyUpgrades does not mark the new versions as resolved, so they can still1464// be further modified by other queries (such as wildcards).1465//1466// If all pathSets are resolved without any changes to the build list,1467// applyUpgrades returns with changed=false.1468func (r *resolver) applyUpgrades(ld *modload.Loader, ctx context.Context, upgrades []pathSet) (changed bool) {1469	defer base.ExitIfErrors()14701471	// Arbitrarily add a "latest" version that provides each missing package, but1472	// do not mark the version as resolved: we still want to allow the explicit1473	// queries to modify the resulting versions.1474	var tentative []module.Version1475	for _, cs := range upgrades {1476		if cs.err != nil {1477			base.Error(cs.err)1478			continue1479		}14801481		filtered, _, m, unique := r.disambiguate(ld, cs)1482		if !unique {1483			_, m = r.chooseArbitrarily(filtered)1484		}1485		if m.Path == "" {1486			// There is no viable candidate for the missing package.1487			// Leave it unresolved.1488			continue1489		}1490		tentative = append(tentative, m)1491	}1492	base.ExitIfErrors()14931494	changed = r.updateBuildList(ld, ctx, tentative)1495	return changed1496}14971498// disambiguate eliminates candidates from cs that conflict with other module1499// versions that have already been resolved. If there is only one (unique)1500// remaining candidate, disambiguate returns that candidate, along with1501// an indication of whether that result interprets cs.path as a package1502//1503// Note: we're only doing very simple disambiguation here. The goal is to1504// reproduce the user's intent, not to find a solution that a human couldn't.1505// In the vast majority of cases, we expect only one module per pathSet,1506// but we want to give some minimal additional tools so that users can add an1507// extra argument or two on the command line to resolve simple ambiguities.1508func (r *resolver) disambiguate(s *modload.Loader, cs pathSet) (filtered pathSet, isPackage bool, m module.Version, unique bool) {1509	if len(cs.pkgMods) == 0 && cs.mod.Path == "" {1510		panic("internal error: resolveIfUnambiguous called with empty pathSet")1511	}15121513	for _, m := range cs.pkgMods {1514		if _, ok := r.noneForPath(m.Path); ok {1515			// A query with version "none" forces the candidate module to version1516			// "none", so we cannot use any other version for that module.1517			continue1518		}15191520		if s.MainModules.Contains(m.Path) {1521			if m.Version == "" {1522				return pathSet{}, true, m, true1523			}1524			// A main module can only be set to its own version.1525			continue1526		}15271528		vr, ok := r.resolvedVersion[m.Path]1529		if !ok {1530			// m is a viable answer to the query, but other answers may also1531			// still be viable.1532			filtered.pkgMods = append(filtered.pkgMods, m)1533			continue1534		}15351536		if vr.version != m.Version {1537			// Some query forces the candidate module to a version other than this1538			// one.1539			//1540			// The command could be something like1541			//1542			// 	go get example.com/foo/bar@none example.com/foo/bar/baz@latest1543			//1544			// in which case we *cannot* resolve the package from1545			// example.com/foo/bar (because it is constrained to version1546			// "none") and must fall through to module example.com/foo@latest.1547			continue1548		}15491550		// Some query forces the candidate module *to* the candidate version.1551		// As a result, this candidate is the only viable choice to provide1552		// its package(s): any other choice would result in an ambiguous import1553		// for this path.1554		//1555		// For example, consider the command1556		//1557		// 	go get example.com/foo@latest example.com/foo/bar/baz@latest1558		//1559		// If modules example.com/foo and example.com/foo/bar both provide1560		// package example.com/foo/bar/baz, then we *must* resolve the package1561		// from example.com/foo: if we instead resolved it from1562		// example.com/foo/bar, we would have two copies of the package.1563		return pathSet{}, true, m, true1564	}15651566	if cs.mod.Path != "" {1567		vr, ok := r.resolvedVersion[cs.mod.Path]1568		if !ok || vr.version == cs.mod.Version {1569			filtered.mod = cs.mod1570		}1571	}15721573	if len(filtered.pkgMods) == 1 &&1574		(filtered.mod.Path == "" || filtered.mod == filtered.pkgMods[0]) {1575		// Exactly one viable module contains the package with the given path1576		// (by far the common case), so we can resolve it unambiguously.1577		return pathSet{}, true, filtered.pkgMods[0], true1578	}15791580	if len(filtered.pkgMods) == 0 {1581		// All modules that could provide the path as a package conflict with other1582		// resolved arguments. If it can refer to a module instead, return that;1583		// otherwise, this pathSet cannot be resolved (and we will return the1584		// zero module.Version).1585		return pathSet{}, false, filtered.mod, true1586	}15871588	// The query remains ambiguous: there are at least two different modules1589	// to which cs.path could refer.1590	return filtered, false, module.Version{}, false1591}15921593// chooseArbitrarily returns an arbitrary (but deterministic) module version1594// from among those in the given set.1595//1596// chooseArbitrarily prefers module paths that were already in the build list at1597// the start of 'go get', prefers modules that provide packages over those that1598// do not, and chooses the first module meeting those criteria (so biases toward1599// longer paths).1600func (r *resolver) chooseArbitrarily(cs pathSet) (isPackage bool, m module.Version) {1601	// Prefer to upgrade some module that was already in the build list.1602	for _, m := range cs.pkgMods {1603		if r.initialSelected(m.Path) != "none" {1604			return true, m1605		}1606	}16071608	// Otherwise, arbitrarily choose the first module that provides the package.1609	if len(cs.pkgMods) > 0 {1610		return true, cs.pkgMods[0]1611	}16121613	return false, cs.mod1614}16151616// checkPackageProblems reloads packages for the given patterns and reports1617// missing and ambiguous package errors. It also reports retractions and1618// deprecations for resolved modules and modules needed to build named packages.1619// It also adds a sum for each updated module in the build list if we had one1620// before and didn't get one while loading packages.1621//1622// We skip missing-package errors earlier in the process, since we want to1623// resolve pathSets ourselves, but at that point, we don't have enough context1624// to log the package-import chains leading to each error.1625func (r *resolver) checkPackageProblems(ld *modload.Loader, ctx context.Context, pkgPatterns []string) {1626	defer base.ExitIfErrors()16271628	// Enter workspace mode, if the current main module would belong to it, when1629	// doing the workspace load. We want to check that the workspace loads properly1630	// and doesn't have missing or ambiguous imports (rather than checking the module1631	// by itself) because the module may have unreleased dependencies in the workspace.1632	// We'll also report issues for retracted and deprecated modules using the workspace1633	// info, but switch back to single module mode when fetching sums so that we update1634	// the single module's go.sum file.1635	var exitWorkspace func()1636	if r.workspace != nil && r.workspace.hasModule(ld.MainModules.Versions()[0].Path) {1637		var err error1638		exitWorkspace, err = modload.EnterWorkspace(ld, ctx)1639		if err != nil {1640			// A TooNewError can happen for1641			// go get go@newversion when all the required modules1642			// are old enough but the go command itself is not new1643			// enough. See the related comment on the SwitchOrFatal1644			// in runGet when WriteGoMod returns an error.1645			toolchain.SwitchOrFatal(ld, ctx, err)1646		}1647	}16481649	// Gather information about modules we might want to load retractions and1650	// deprecations for. Loading this metadata requires at least one version1651	// lookup per module, and we don't want to load information that's neither1652	// relevant nor actionable.1653	type modFlags int1654	const (1655		resolved modFlags = 1 << iota // version resolved by 'go get'1656		named                         // explicitly named on command line or provides a named package1657		hasPkg                        // needed to build named packages1658		direct                        // provides a direct dependency of the main module or workspace modules1659	)1660	relevantMods := make(map[module.Version]modFlags)1661	for path, reason := range r.resolvedVersion {1662		m := module.Version{Path: path, Version: reason.version}1663		relevantMods[m] |= resolved1664	}16651666	// Reload packages, reporting errors for missing and ambiguous imports.1667	if len(pkgPatterns) > 0 {1668		// LoadPackages will print errors (since it has more context) but will not1669		// exit, since we need to load retractions later.1670		pkgOpts := modload.PackageOpts{1671			VendorModulesInGOROOTSrc: true,1672			LoadTests:                *getT,1673			ResolveMissingImports:    false,1674			AllowErrors:              true,1675			SilenceNoGoErrors:        true,1676		}1677		matches, pkgs := modload.LoadPackages(ld, ctx, pkgOpts, pkgPatterns...)1678		for _, m := range matches {1679			if len(m.Errs) > 0 {1680				base.SetExitStatus(1)1681				break1682			}1683		}1684		for _, pkg := range pkgs {1685			if dir, _, err := modload.Lookup(ld, "", false, pkg); err != nil {1686				if dir != "" && errors.Is(err, imports.ErrNoGo) {1687					// Since dir is non-empty, we must have located source files1688					// associated with either the package or its test — ErrNoGo must1689					// indicate that none of those source files happen to apply in this1690					// configuration. If we are actually building the package (no -d1691					// flag), we will report the problem then; otherwise, assume that the1692					// user is going to build or test this package in some other1693					// configuration and suppress the error.1694					continue1695				}16961697				base.SetExitStatus(1)1698				if ambiguousErr, ok := errors.AsType[*modload.AmbiguousImportError](err); ok {1699					for _, m := range ambiguousErr.Modules {1700						relevantMods[m] |= hasPkg1701					}1702				}1703			}1704			if m := ld.PackageModule(pkg); m.Path != "" {1705				relevantMods[m] |= hasPkg1706			}1707		}1708		for _, match := range matches {1709			for _, pkg := range match.Pkgs {1710				m := ld.PackageModule(pkg)1711				relevantMods[m] |= named1712			}1713		}1714	}17151716	reqs := modload.LoadModFile(ld, ctx)1717	for m := range relevantMods {1718		if reqs.IsDirect(m.Path) {1719			relevantMods[m] |= direct1720		}1721	}17221723	// Load retractions for modules mentioned on the command line and modules1724	// needed to build named packages. We care about retractions of indirect1725	// dependencies, since we might be able to upgrade away from them.1726	type modMessage struct {1727		m       module.Version1728		message string1729	}1730	retractions := make([]modMessage, 0, len(relevantMods))1731	for m, flags := range relevantMods {1732		if flags&(resolved|named|hasPkg) != 0 {1733			retractions = append(retractions, modMessage{m: m})1734		}1735	}1736	sort.Slice(retractions, func(i, j int) bool { return retractions[i].m.Path < retractions[j].m.Path })1737	for i := range retractions {1738		i := i1739		r.work.Add(func() {1740			err := ld.CheckRetractions(ctx, retractions[i].m)1741			if _, ok := errors.AsType[*modload.ModuleRetractedError](err); ok {1742				retractions[i].message = err.Error()1743			}1744		})1745	}17461747	// Load deprecations for modules mentioned on the command line. Only load1748	// deprecations for indirect dependencies if they're also direct dependencies1749	// of the main module or workspace modules. Deprecations of purely indirect1750	// dependencies are not actionable.1751	deprecations := make([]modMessage, 0, len(relevantMods))1752	for m, flags := range relevantMods {1753		if flags&(resolved|named) != 0 || flags&(hasPkg|direct) == hasPkg|direct {1754			deprecations = append(deprecations, modMessage{m: m})1755		}1756	}1757	sort.Slice(deprecations, func(i, j int) bool { return deprecations[i].m.Path < deprecations[j].m.Path })1758	for i := range deprecations {1759		i := i1760		r.work.Add(func() {1761			deprecation, err := modload.CheckDeprecation(ld, ctx, deprecations[i].m)1762			if err != nil || deprecation == "" {1763				return1764			}1765			deprecations[i].message = modload.ShortMessage(deprecation, "")1766		})1767	}17681769	// exit the workspace if we had entered it earlier. We want to add the sums1770	// to the go.sum file for the module we're running go get from.1771	if exitWorkspace != nil {1772		// Wait for retraction and deprecation checks (that depend on the global1773		// modload state containing the workspace) to finish before we reset the1774		// state back to single module mode.1775		<-r.work.Idle()1776		exitWorkspace()1777	}17781779	// Load sums for updated modules that had sums before. When we update a1780	// module, we may update another module in the build list that provides a1781	// package in 'all' that wasn't loaded as part of this 'go get' command.1782	// If we don't add a sum for that module, builds may fail later.1783	// Note that an incidentally updated package could still import packages1784	// from unknown modules or from modules in the build list that we didn't1785	// need previously. We can't handle that case without loading 'all'.1786	sumErrs := make([]error, len(r.buildList))1787	for i := range r.buildList {1788		i := i1789		m := r.buildList[i]1790		mActual := m1791		if mRepl := modload.Replacement(ld, m); mRepl.Path != "" {1792			mActual = mRepl1793		}1794		old := module.Version{Path: m.Path, Version: r.initialVersion[m.Path]}1795		if old.Version == "" {1796			continue1797		}1798		oldActual := old1799		if oldRepl := modload.Replacement(ld, old); oldRepl.Path != "" {1800			oldActual = oldRepl1801		}1802		if mActual == oldActual || mActual.Version == "" || !modfetch.HaveSum(ld.Fetcher(), oldActual) {1803			continue1804		}1805		r.work.Add(func() {1806			if _, err := ld.Fetcher().DownloadZip(ctx, mActual); err != nil {1807				verb := "upgraded"1808				if gover.ModCompare(m.Path, m.Version, old.Version) < 0 {1809					verb = "downgraded"1810				}1811				replaced := ""1812				if mActual != m {1813					replaced = fmt.Sprintf(" (replaced by %s)", mActual)1814				}1815				err = fmt.Errorf("%s %s %s => %s%s: error finding sum for %s: %v", verb, m.Path, old.Version, m.Version, replaced, mActual, err)1816				sumErrs[i] = err1817			}1818		})1819	}18201821	<-r.work.Idle()18221823	// Report deprecations, then retractions, then errors fetching sums.1824	// Only errors fetching sums are hard errors.1825	for _, mm := range deprecations {1826		if mm.message != "" {1827			fmt.Fprintf(os.Stderr, "go: module %s is deprecated: %s\n", mm.m.Path, mm.message)1828		}1829	}1830	var retractPath string1831	for _, mm := range retractions {1832		if mm.message != "" {1833			fmt.Fprintf(os.Stderr, "go: warning: %v\n", mm.message)1834			if retractPath == "" {1835				retractPath = mm.m.Path1836			} else {1837				retractPath = "<module>"1838			}1839		}1840	}1841	if retractPath != "" {1842		fmt.Fprintf(os.Stderr, "go: to switch to the latest unretracted version, run:\n\tgo get %s@latest\n", retractPath)1843	}1844	for _, err := range sumErrs {1845		if err != nil {1846			base.Error(err)1847		}1848	}1849}18501851// reportChanges logs version changes to os.Stderr.1852//1853// reportChanges only logs changes to modules named on the command line and to1854// explicitly required modules in go.mod. Most changes to indirect requirements1855// are not relevant to the user and are not logged.1856//1857// reportChanges should be called after WriteGoMod.1858func (r *resolver) reportChanges(oldReqs, newReqs []module.Version) {1859	type change struct {1860		path, old, new string1861	}1862	changes := make(map[string]change)18631864	// Collect changes in modules matched by command line arguments.1865	for path, reason := range r.resolvedVersion {1866		if gover.IsToolchain(path) {1867			continue1868		}1869		old := r.initialVersion[path]1870		new := reason.version1871		if old != new && (old != "" || new != "none") {1872			changes[path] = change{path, old, new}1873		}1874	}18751876	// Collect changes to explicit requirements in go.mod.1877	for _, req := range oldReqs {1878		if gover.IsToolchain(req.Path) {1879			continue1880		}1881		path := req.Path1882		old := req.Version1883		new := r.buildListVersion[path]1884		if old != new {1885			changes[path] = change{path, old, new}1886		}1887	}1888	for _, req := range newReqs {1889		if gover.IsToolchain(req.Path) {1890			continue1891		}1892		path := req.Path1893		old := r.initialVersion[path]1894		new := req.Version1895		if old != new {1896			changes[path] = change{path, old, new}1897		}1898	}18991900	// Toolchain diffs are easier than requirements: diff old and new directly.1901	toolchainVersions := func(reqs []module.Version) (goV, toolchain string) {1902		for _, req := range reqs {1903			if req.Path == "go" {1904				goV = req.Version1905			}1906			if req.Path == "toolchain" {1907				toolchain = req.Version1908			}1909		}1910		return1911	}1912	oldGo, oldToolchain := toolchainVersions(oldReqs)1913	newGo, newToolchain := toolchainVersions(newReqs)1914	if oldGo != newGo {1915		changes["go"] = change{"go", oldGo, newGo}1916	}1917	if oldToolchain != newToolchain {1918		changes["toolchain"] = change{"toolchain", oldToolchain, newToolchain}1919	}19201921	sortedChanges := make([]change, 0, len(changes))1922	for _, c := range changes {1923		sortedChanges = append(sortedChanges, c)1924	}1925	sort.Slice(sortedChanges, func(i, j int) bool {1926		pi := sortedChanges[i].path1927		pj := sortedChanges[j].path1928		if pi == pj {1929			return false1930		}1931		// go first; toolchain second1932		switch {1933		case pi == "go":1934			return true1935		case pj == "go":1936			return false1937		case pi == "toolchain":1938			return true1939		case pj == "toolchain":1940			return false1941		}1942		return pi < pj1943	})19441945	for _, c := range sortedChanges {1946		if c.old == "" {1947			fmt.Fprintf(os.Stderr, "go: added %s %s\n", c.path, c.new)1948		} else if c.new == "none" || c.new == "" {1949			fmt.Fprintf(os.Stderr, "go: removed %s %s\n", c.path, c.old)1950		} else if gover.ModCompare(c.path, c.new, c.old) > 0 {1951			fmt.Fprintf(os.Stderr, "go: upgraded %s %s => %s\n", c.path, c.old, c.new)1952			if c.path == "go" && gover.Compare(c.old, gover.ExplicitIndirectVersion) < 0 && gover.Compare(c.new, gover.ExplicitIndirectVersion) >= 0 {1953				fmt.Fprintf(os.Stderr, "\tnote: expanded dependencies to upgrade to go %s or higher; run 'go mod tidy' to clean up\n", gover.ExplicitIndirectVersion)1954			}19551956		} else {1957			fmt.Fprintf(os.Stderr, "go: downgraded %s %s => %s\n", c.path, c.old, c.new)1958		}1959	}19601961	// TODO(golang.org/issue/33284): attribute changes to command line arguments.1962	// For modules matched by command line arguments, this probably isn't1963	// necessary, but it would be useful for unmatched direct dependencies of1964	// the main module.1965}19661967// resolve records that module m must be at its indicated version (which may be1968// "none") due to query q. If some other query forces module m to be at a1969// different version, resolve reports a conflict error.1970func (r *resolver) resolve(s *modload.Loader, q *query, m module.Version) {1971	if m.Path == "" {1972		panic("internal error: resolving a module.Version with an empty path")1973	}19741975	if s.MainModules.Contains(m.Path) && m.Version != "" {1976		reportError(q, &modload.QueryMatchesMainModulesError{1977			MainModules:     []module.Version{{Path: m.Path}},1978			Pattern:         q.pattern,1979			Query:           q.version,1980			PatternIsModule: s.MainModules.Contains(q.pattern),1981		})1982		return1983	}19841985	vr, ok := r.resolvedVersion[m.Path]1986	if ok && vr.version != m.Version {1987		reportConflict(q, m, vr)1988		return1989	}1990	r.resolvedVersion[m.Path] = versionReason{m.Version, q}1991	q.resolved = append(q.resolved, m)1992}19931994// updateBuildList updates the module loader's global build list to be1995// consistent with r.resolvedVersion, and to include additional modules1996// provided that they do not conflict with the resolved versions.1997//1998// If the additional modules conflict with the resolved versions, they will be1999// downgraded to a non-conflicting version (possibly "none").2000//

Code quality findings 85

Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
UsageLine: "go get [-t] [-u] [-tool] [build flags] [packages]",
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
go get example.com/pkg
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
go get example.com/pkg@v1.2.3
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
go get example.com/mod@none
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
go get go@latest
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
go get toolchain@patch
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
In earlier versions of Go, 'go get' was used to build and install packages.
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
Now, 'go get' is dedicated to adjusting dependencies in go.mod. 'go install'
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
'go install' runs in module-aware mode and ignores the go.mod file in the
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
go install example.com/pkg@v1.2.3
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
go install example.com/pkg@latest
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
See 'go help install' or https://go.dev/ref/mod#go-install for details.
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
'go get' accepts the following flags.
Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
go command to download the module using the GOPROXY protocol.
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer base.ExitIfErrors()
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer base.ExitIfErrors()
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer base.ExitIfErrors()
Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer base.ExitIfErrors()
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Hidden side effects; favor explicit initialization in main() or functions
info correctness func-init
func init() {
Infinite loop detected; ensure it has a proper exit condition (e.g., break, return) to avoid unintentional resource consumption or hangs
info correctness infinite-loop
for {
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
pkgPatterns = append(pkgPatterns, q.pattern)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
patterns = append(patterns, q.pattern)
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, m := range matches {
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
opts.DropTools = append(opts.DropTools, m.Pkgs...)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
opts.AddTools = append(opts.AddTools, m.Pkgs...)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
queries = append(queries, q)
Manual scheduling hint; usually unnecessary and indicates deeper design issues
info correctness manual-scheduling
work: par.NewQueue(runtime.GOMAXPROCS(0)),
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
r.patternAllQueries = append(r.patternAllQueries, q)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
r.workQueries = append(r.workQueries, q)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
r.toolQueries = append(r.toolQueries, q)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
r.localQueries = append(r.localQueries, q)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
r.wildcardQueries = append(r.wildcardQueries, q)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
r.pathQueries = append(r.pathQueries, q)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
r.wildcardNones = append(r.wildcardNones, q)
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if search.IsMetaPackage(q.pattern) {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if !q.isWildcard() {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if hasModRoot && ld.MainModules.Contains(q.pattern) {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
// resolve new dependencies if nothing else turns up.
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for _, curM := range r.buildList {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if !q.canMatchInModule(curM.Path) {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if _, hit := r.noneForPath(curM.Path); hit {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if ld.MainModules.Contains(curM.Path) && !versionOkForMainModule(q.version) {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if q.version == "none" || q.matchesPackages {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if err != nil {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if isNoSuchPackageVersion(err) && len(q.resolved) > 0 {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for _, curM := range r.buildList {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if !q.canMatchInModule(curM.Path) {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if !q.matchesPath(curM.Path) {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if len(packages) == 0 {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if err != nil {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if q.version == "none" {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if search.IsStandardImportPath(q.pattern) {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if len(r.patternAllQueries) == 0 {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for _, q := range r.patternAllQueries {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if len(pkgMods) != 1 || pkgMods[0] != m {
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
patterns = append(patterns, q.pattern)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
upgrades = append(upgrades, pathSet{path: path, pkgMods: pkgMods, err: err})
Infinite loop detected; ensure it has a proper exit condition (e.g., break, return) to avoid unintentional resource consumption or hangs
info correctness infinite-loop
for {
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
unresolved = append(unresolved, filtered)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
tentative = append(tentative, m)
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for _, m := range cs.pkgMods {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if _, ok := r.noneForPath(m.Path); ok {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
// "none", so we cannot use any other version for that module.
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
filtered.pkgMods = append(filtered.pkgMods, m)
Map created without size hint before being populated in a loop; provide capacity hint to reduce allocations
info performance map-without-size-hint
relevantMods := make(map[module.Version]modFlags)
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for path, reason := range r.resolvedVersion {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for m, flags := range relevantMods {
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
retractions = append(retractions, modMessage{m: m})
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for m, flags := range relevantMods {
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
deprecations = append(deprecations, modMessage{m: m})
Map created without size hint before being populated in a loop; provide capacity hint to reduce allocations
info performance map-without-size-hint
changes := make(map[string]change)
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for path, reason := range r.resolvedVersion {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for mPath, rv := range r.resolvedVersion {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, r := range f.Require {
Map created without size hint before being populated in a loop; provide capacity hint to reduce allocations
info performance map-without-size-hint
w := &workspace{modules: make(map[string]string)}
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
modFile := filepath.Join(modRoot, "go.mod")
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for modPath, modroot := range w.modules {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.