@@ -14,6 +14,7 @@ import (
14
14
"path"
15
15
"sort"
16
16
"strconv"
17
+ "strings"
17
18
"sync"
18
19
"time"
19
20
@@ -184,7 +185,8 @@ func FetchModule(ctx context.Context, modulePath, requestedVersion string, proxy
184
185
185
186
// getGoModPath may return a non-empty goModPath even if the error is
186
187
// non-nil, if the module version is an alternative module.
187
- fr .GoModPath , fr .Error = getGoModPath (ctx , modulePath , fr .ResolvedVersion , proxyClient )
188
+ var goModBytes []byte
189
+ fr .GoModPath , goModBytes , fr .Error = getGoModPath (ctx , modulePath , fr .ResolvedVersion , proxyClient )
188
190
if fr .Error != nil {
189
191
return fr
190
192
}
@@ -213,6 +215,12 @@ func FetchModule(ctx context.Context, modulePath, requestedVersion string, proxy
213
215
fr .Error = err
214
216
return fr
215
217
}
218
+ if goModBytes != nil {
219
+ if err := processGoModFile (goModBytes , mod ); err != nil {
220
+ fr .Error = fmt .Errorf ("%v: %w" , err .Error (), derrors .BadModule )
221
+ return fr
222
+ }
223
+ }
216
224
fr .Module = mod
217
225
fr .PackageVersionStates = pvs
218
226
if modulePath == stdlib .ModulePath {
@@ -252,26 +260,27 @@ func getZipSize(ctx context.Context, modulePath, resolvedVersion string, proxyCl
252
260
return proxyClient .GetZipSize (ctx , modulePath , resolvedVersion )
253
261
}
254
262
255
- func getGoModPath (ctx context.Context , modulePath , resolvedVersion string , proxyClient * proxy.Client ) (_ string , err error ) {
263
+ // getGoModPath returns the module path from the go.mod file, as well as the contents of the file obtained from the proxy.
264
+ // If modulePath is the standardl library, then the contents will be nil.
265
+ func getGoModPath (ctx context.Context , modulePath , resolvedVersion string , proxyClient * proxy.Client ) (string , []byte , error ) {
256
266
if modulePath == stdlib .ModulePath {
257
- return stdlib .ModulePath , nil
267
+ return stdlib .ModulePath , nil , nil
258
268
}
259
-
260
269
goModBytes , err := proxyClient .GetMod (ctx , modulePath , resolvedVersion )
261
270
if err != nil {
262
- return "" , err
271
+ return "" , nil , err
263
272
}
264
273
goModPath := modfile .ModulePath (goModBytes )
265
274
if goModPath == "" {
266
- return "" , fmt .Errorf ("go.mod has no module path: %w" , derrors .BadModule )
275
+ return "" , nil , fmt .Errorf ("go.mod has no module path: %w" , derrors .BadModule )
267
276
}
268
277
if goModPath != modulePath {
269
278
// The module path in the go.mod file doesn't match the path of the
270
279
// zip file. Don't insert the module. Store an AlternativeModule
271
280
// status in module_version_states.
272
- return goModPath , fmt .Errorf ("module path=%s, go.mod path=%s: %w" , modulePath , goModPath , derrors .AlternativeModule )
281
+ return goModPath , goModBytes , fmt .Errorf ("module path=%s, go.mod path=%s: %w" , modulePath , goModPath , derrors .AlternativeModule )
273
282
}
274
- return goModPath , nil
283
+ return goModPath , goModBytes , nil
275
284
}
276
285
277
286
// processZipFile extracts information from the module version zip.
@@ -301,8 +310,7 @@ func processZipFile(ctx context.Context, modulePath string, resolvedVersion stri
301
310
if err != nil {
302
311
return nil , nil , fmt .Errorf ("extractPackagesFromZip(%q, %q, zipReader, %v): %v" , modulePath , resolvedVersion , allLicenses , err )
303
312
}
304
- hasGoMod := zipContainsFilename (zipReader , path .Join (moduleVersionDir (modulePath , resolvedVersion ), "go.mod" ))
305
-
313
+ hasGoMod := zipFile (zipReader , path .Join (moduleVersionDir (modulePath , resolvedVersion ), "go.mod" )) != nil
306
314
return & internal.Module {
307
315
ModuleInfo : internal.ModuleInfo {
308
316
ModulePath : modulePath ,
@@ -317,20 +325,54 @@ func processZipFile(ctx context.Context, modulePath string, resolvedVersion stri
317
325
}, packageVersionStates , nil
318
326
}
319
327
328
+ // processGoModFile populates mod with information extracted from the contents of the go.mod file.
329
+ func processGoModFile (goModBytes []byte , mod * internal.Module ) (err error ) {
330
+ defer derrors .Wrap (& err , "processGoModFile" )
331
+
332
+ mf , err := modfile .Parse ("go.mod" , goModBytes , nil )
333
+ if err != nil {
334
+ return err
335
+ }
336
+ hasDepComment , depComment := extractDeprecatedComment (mf )
337
+ if hasDepComment {
338
+ mod .DeprecatedComment = & depComment
339
+ }
340
+ return nil
341
+ }
342
+
343
+ // extractDeprecatedComment looks for "Deprecated" comments in the line comments
344
+ // before the module declaration. If it finds one, it returns true along with
345
+ // the text after "Deprecated:". Otherwise it returns false, "".
346
+ func extractDeprecatedComment (mf * modfile.File ) (bool , string ) {
347
+ const prefix = "Deprecated:"
348
+
349
+ if mf .Module == nil {
350
+ return false , ""
351
+ }
352
+ for _ , comment := range append (mf .Module .Syntax .Before , mf .Module .Syntax .Suffix ... ) {
353
+ text := strings .TrimSpace (strings .TrimPrefix (comment .Token , "//" ))
354
+ if strings .HasPrefix (text , prefix ) {
355
+ return true , strings .TrimSpace (text [len (prefix ):])
356
+ }
357
+ }
358
+ return false , ""
359
+ }
360
+
320
361
// moduleVersionDir formats the content subdirectory for the given
321
362
// modulePath and version.
322
363
func moduleVersionDir (modulePath , version string ) string {
323
364
return fmt .Sprintf ("%s@%s" , modulePath , version )
324
365
}
325
366
326
- // zipContainsFilename reports whether there is a file with the given name in the zip.
327
- func zipContainsFilename (r * zip.Reader , name string ) bool {
367
+ // zipFile returns the file in r whose name matches the given name, or nil
368
+ // if there isn't one.
369
+ func zipFile (r * zip.Reader , name string ) * zip.File {
328
370
for _ , f := range r .File {
329
371
if f .Name == name {
330
- return true
372
+ return f
331
373
}
332
374
}
333
- return false
375
+ return nil
334
376
}
335
377
336
378
type FetchInfo struct {
0 commit comments