1 // Copyright 2023 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.
12 "cmd/go/internal/base"
13 "cmd/go/internal/gover"
16 // A Switcher collects errors to be reported and then decides
17 // between reporting the errors or switching to a new toolchain
20 // The client calls [Switcher.Error] repeatedly with errors encountered
21 // and then calls [Switcher.Switch]. If the errors included any
22 // *gover.TooNewErrors (potentially wrapped) and switching is
23 // permitted by GOTOOLCHAIN, Switch switches to a new toolchain.
24 // Otherwise Switch prints all the errors using base.Error.
25 type Switcher struct {
26 TooNew *gover.TooNewError // max go requirement observed
27 Errors []error // errors collected so far
30 // Error reports the error to the Switcher,
31 // which saves it for processing during Switch.
32 func (s *Switcher) Error(err error) {
33 s.Errors = append(s.Errors, err)
37 // addTooNew adds any TooNew errors that can be found in err.
38 func (s *Switcher) addTooNew(err error) {
39 switch err := err.(type) {
40 case interface{ Unwrap() []error }:
41 for _, e := range err.Unwrap() {
45 case interface{ Unwrap() error }:
46 s.addTooNew(err.Unwrap())
48 case *gover.TooNewError:
50 gover.Compare(err.GoVersion, s.TooNew.GoVersion) > 0 ||
51 gover.Compare(err.GoVersion, s.TooNew.GoVersion) == 0 && err.What < s.TooNew.What {
57 // NeedSwitch reports whether Switch would attempt to switch toolchains.
58 func (s *Switcher) NeedSwitch() bool {
59 return s.TooNew != nil && (HasAuto() || HasPath())
62 // Switch decides whether to switch to a newer toolchain
63 // to resolve any of the saved errors.
64 // It switches if toolchain switches are permitted and there is at least one TooNewError.
66 // If Switch decides not to switch toolchains, it prints the errors using base.Error and returns.
68 // If Switch decides to switch toolchains but cannot identify a toolchain to use.
69 // it prints the errors along with one more about not being able to find the toolchain
72 // Otherwise, Switch prints an informational message giving a reason for the
73 // switch and the toolchain being invoked and then switches toolchains.
74 // This operation never returns.
75 func (s *Switcher) Switch(ctx context.Context) {
77 for _, err := range s.Errors {
83 // Switch to newer Go toolchain if necessary and possible.
84 tv, err := NewerToolchain(ctx, s.TooNew.GoVersion)
86 for _, err := range s.Errors {
89 base.Error(fmt.Errorf("switching to go >= %v: %w", s.TooNew.GoVersion, err))
93 fmt.Fprintf(os.Stderr, "go: %v requires go >= %v; switching to %v\n", s.TooNew.What, s.TooNew.GoVersion, tv)
98 // SwitchOrFatal attempts a toolchain switch based on the information in err
99 // and otherwise falls back to base.Fatal(err).
100 func SwitchOrFatal(ctx context.Context, err error) {