Skip to content

Commit 6ed045b

Browse files
author
Jay Conrod
committed
cmd/go: refactor modload.CheckRetractions
Extract queryLatestVersionIgnoringRetractions, which returns the version we should load retractions and deprecations from. This will be shared with CheckDeprecations. Rename ShortRetractionRationale to ShortMessage. This will be used to shorten deprecation warnings as well. For #40357 Change-Id: Ic1e0c670396bdb3bd87c7a97cf2b14ca58ea1d80 Reviewed-on: https://go-review.googlesource.com/c/go/+/306332 Trust: Jay Conrod <[email protected]> Run-TryBot: Jay Conrod <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Michael Matloob <[email protected]>
1 parent ee51e3d commit 6ed045b

File tree

2 files changed

+104
-79
lines changed

2 files changed

+104
-79
lines changed

src/cmd/go/internal/modload/modfile.go

+102-77
Original file line numberDiff line numberDiff line change
@@ -92,73 +92,53 @@ func (e *excludedError) Is(err error) bool { return err == ErrDisallowed }
9292

9393
// CheckRetractions returns an error if module m has been retracted by
9494
// its author.
95-
func CheckRetractions(ctx context.Context, m module.Version) error {
95+
func CheckRetractions(ctx context.Context, m module.Version) (err error) {
96+
defer func() {
97+
if retractErr := (*ModuleRetractedError)(nil); err == nil || errors.As(err, &retractErr) {
98+
return
99+
}
100+
// Attribute the error to the version being checked, not the version from
101+
// which the retractions were to be loaded.
102+
if mErr := (*module.ModuleError)(nil); errors.As(err, &mErr) {
103+
err = mErr.Err
104+
}
105+
err = &retractionLoadingError{m: m, err: err}
106+
}()
107+
96108
if m.Version == "" {
97109
// Main module, standard library, or file replacement module.
98110
// Cannot be retracted.
99111
return nil
100112
}
101-
102-
// Look up retraction information from the latest available version of
103-
// the module. Cache retraction information so we don't parse the go.mod
104-
// file repeatedly.
105-
type entry struct {
106-
retract []retraction
107-
err error
113+
if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
114+
// All versions of the module were replaced.
115+
// Don't load retractions, since we'd just load the replacement.
116+
return nil
108117
}
109-
path := m.Path
110-
e := retractCache.Do(path, func() (v interface{}) {
111-
ctx, span := trace.StartSpan(ctx, "checkRetractions "+path)
112-
defer span.Done()
113-
114-
if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
115-
// All versions of the module were replaced with a local directory.
116-
// Don't load retractions.
117-
return &entry{nil, nil}
118-
}
119118

120-
// Find the latest version of the module.
121-
// Ignore exclusions from the main module's go.mod.
122-
const ignoreSelected = ""
123-
var allowAll AllowedFunc
124-
rev, err := Query(ctx, path, "latest", ignoreSelected, allowAll)
125-
if err != nil {
126-
return &entry{nil, err}
127-
}
128-
129-
// Load go.mod for that version.
130-
// If the version is replaced, we'll load retractions from the replacement.
131-
//
132-
// If there's an error loading the go.mod, we'll return it here.
133-
// These errors should generally be ignored by callers of checkRetractions,
134-
// since they happen frequently when we're offline. These errors are not
135-
// equivalent to ErrDisallowed, so they may be distinguished from
136-
// retraction errors.
137-
//
138-
// We load the raw file here: the go.mod file may have a different module
139-
// path that we expect if the module or its repository was renamed.
140-
// We still want to apply retractions to other aliases of the module.
141-
rm := resolveReplacement(module.Version{Path: path, Version: rev.Version})
142-
summary, err := rawGoModSummary(rm)
143-
if err != nil {
144-
return &entry{nil, err}
145-
}
146-
return &entry{summary.retract, nil}
147-
}).(*entry)
148-
149-
if err := e.err; err != nil {
150-
// Attribute the error to the version being checked, not the version from
151-
// which the retractions were to be loaded.
152-
var mErr *module.ModuleError
153-
if errors.As(err, &mErr) {
154-
err = mErr.Err
155-
}
156-
return &retractionLoadingError{m: m, err: err}
119+
// Find the latest available version of the module, and load its go.mod. If
120+
// the latest version is replaced, we'll load the replacement.
121+
//
122+
// If there's an error loading the go.mod, we'll return it here. These errors
123+
// should generally be ignored by callers since they happen frequently when
124+
// we're offline. These errors are not equivalent to ErrDisallowed, so they
125+
// may be distinguished from retraction errors.
126+
//
127+
// We load the raw file here: the go.mod file may have a different module
128+
// path that we expect if the module or its repository was renamed.
129+
// We still want to apply retractions to other aliases of the module.
130+
rm, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
131+
if err != nil {
132+
return err
133+
}
134+
summary, err := rawGoModSummary(rm)
135+
if err != nil {
136+
return err
157137
}
158138

159139
var rationale []string
160140
isRetracted := false
161-
for _, r := range e.retract {
141+
for _, r := range summary.retract {
162142
if semver.Compare(r.Low, m.Version) <= 0 && semver.Compare(m.Version, r.High) <= 0 {
163143
isRetracted = true
164144
if r.Rationale != "" {
@@ -172,8 +152,6 @@ func CheckRetractions(ctx context.Context, m module.Version) error {
172152
return nil
173153
}
174154

175-
var retractCache par.Cache
176-
177155
type ModuleRetractedError struct {
178156
Rationale []string
179157
}
@@ -183,7 +161,7 @@ func (e *ModuleRetractedError) Error() string {
183161
if len(e.Rationale) > 0 {
184162
// This is meant to be a short error printed on a terminal, so just
185163
// print the first rationale.
186-
msg += ": " + ShortRetractionRationale(e.Rationale[0])
164+
msg += ": " + ShortMessage(e.Rationale[0], "retracted by module author")
187165
}
188166
return msg
189167
}
@@ -205,28 +183,31 @@ func (e *retractionLoadingError) Unwrap() error {
205183
return e.err
206184
}
207185

208-
// ShortRetractionRationale returns a retraction rationale string that is safe
209-
// to print in a terminal. It returns hard-coded strings if the rationale
210-
// is empty, too long, or contains non-printable characters.
211-
func ShortRetractionRationale(rationale string) string {
212-
const maxRationaleBytes = 500
213-
if i := strings.Index(rationale, "\n"); i >= 0 {
214-
rationale = rationale[:i]
215-
}
216-
rationale = strings.TrimSpace(rationale)
217-
if rationale == "" {
218-
return "retracted by module author"
219-
}
220-
if len(rationale) > maxRationaleBytes {
221-
return "(rationale omitted: too long)"
222-
}
223-
for _, r := range rationale {
186+
// ShortMessage returns a string from go.mod (for example, a retraction
187+
// rationale or deprecation message) that is safe to print in a terminal.
188+
//
189+
// If the given string is empty, ShortMessage returns the given default. If the
190+
// given string is too long or contains non-printable characters, ShortMessage
191+
// returns a hard-coded string.
192+
func ShortMessage(message, emptyDefault string) string {
193+
const maxLen = 500
194+
if i := strings.Index(message, "\n"); i >= 0 {
195+
message = message[:i]
196+
}
197+
message = strings.TrimSpace(message)
198+
if message == "" {
199+
return emptyDefault
200+
}
201+
if len(message) > maxLen {
202+
return "(message omitted: too long)"
203+
}
204+
for _, r := range message {
224205
if !unicode.IsGraphic(r) && !unicode.IsSpace(r) {
225-
return "(rationale omitted: contains non-printable characters)"
206+
return "(message omitted: contains non-printable characters)"
226207
}
227208
}
228209
// NOTE: the go.mod parser rejects invalid UTF-8, so we don't check that here.
229-
return rationale
210+
return message
230211
}
231212

232213
// Replacement returns the replacement for mod, if any, from go.mod.
@@ -596,3 +577,47 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) {
596577
}
597578

598579
var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result
580+
581+
// queryLatestVersionIgnoringRetractions looks up the latest version of the
582+
// module with the given path without considering retracted or excluded
583+
// versions.
584+
//
585+
// If all versions of the module are replaced,
586+
// queryLatestVersionIgnoringRetractions returns the replacement without making
587+
// a query.
588+
//
589+
// If the queried latest version is replaced,
590+
// queryLatestVersionIgnoringRetractions returns the replacement.
591+
func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (latest module.Version, err error) {
592+
type entry struct {
593+
latest module.Version
594+
err error
595+
}
596+
e := latestVersionIgnoringRetractionsCache.Do(path, func() interface{} {
597+
ctx, span := trace.StartSpan(ctx, "queryLatestVersionIgnoringRetractions "+path)
598+
defer span.Done()
599+
600+
if repl := Replacement(module.Version{Path: path}); repl.Path != "" {
601+
// All versions of the module were replaced.
602+
// No need to query.
603+
return &entry{latest: repl}
604+
}
605+
606+
// Find the latest version of the module.
607+
// Ignore exclusions from the main module's go.mod.
608+
const ignoreSelected = ""
609+
var allowAll AllowedFunc
610+
rev, err := Query(ctx, path, "latest", ignoreSelected, allowAll)
611+
if err != nil {
612+
return &entry{err: err}
613+
}
614+
latest := module.Version{Path: path, Version: rev.Version}
615+
if repl := resolveReplacement(latest); repl.Path != "" {
616+
latest = repl
617+
}
618+
return &entry{latest: latest}
619+
}).(*entry)
620+
return e.latest, e.err
621+
}
622+
623+
var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersionIgnoringRetractions result

src/cmd/go/testdata/script/mod_retract_rationale.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ cmp stdout multiline
2929

3030
# 'go get' should omit long messages.
3131
go get -d example.com/retract/[email protected]
32-
stderr '^go: warning: example.com/retract/[email protected]: retracted by module author: \(rationale omitted: too long\)'
32+
stderr '^go: warning: example.com/retract/[email protected]: retracted by module author: \(message omitted: too long\)'
3333

3434
# 'go list' should show the full message.
3535
go list -m -retracted -f '{{.Retracted}}' example.com/retract/rationale
@@ -38,7 +38,7 @@ stdout '^\[lo{500}ng\]$'
3838

3939
# 'go get' should omit messages with unprintable characters.
4040
go get -d example.com/retract/[email protected]
41-
stderr '^go: warning: example.com/retract/[email protected]: retracted by module author: \(rationale omitted: contains non-printable characters\)'
41+
stderr '^go: warning: example.com/retract/[email protected]: retracted by module author: \(message omitted: contains non-printable characters\)'
4242

4343
# 'go list' should show the full message.
4444
go list -m -retracted -f '{{.Retracted}}' example.com/retract/rationale

0 commit comments

Comments
 (0)