Skip to content

Commit ff45494

Browse files
AkihiroSudagopherbot
authored andcommitted
go/analysis: expose GoMod etc. to Pass.Module
go.mod (w/ go.sum in the same dir) is needed by https://github.com/AkihiroSuda/gosocialcheck to check the reputation of dependencies. Updates golang/go#73878 Change-Id: Ie589b6d4258263a17698d74ab2f0de23ae7b1a67 GitHub-Last-Rev: 0a3bd49 GitHub-Pull-Request: #577 Reviewed-on: https://go-review.googlesource.com/c/tools/+/676455 Reviewed-by: Cherry Mui <[email protected]> Auto-Submit: Alan Donovan <[email protected]> Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 62daff4 commit ff45494

File tree

3 files changed

+129
-6
lines changed

3 files changed

+129
-6
lines changed

go/analysis/analysis.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"go/token"
1212
"go/types"
1313
"reflect"
14+
"time"
1415
)
1516

1617
// An Analyzer describes an analysis function and its options.
@@ -250,7 +251,19 @@ type Fact interface {
250251

251252
// A Module describes the module to which a package belongs.
252253
type Module struct {
253-
Path string // module path
254-
Version string // module version ("" if unknown, such as for workspace modules)
255-
GoVersion string // go version used in module (e.g. "go1.22.0")
254+
Path string // module path
255+
Version string // module version ("" if unknown, such as for workspace modules)
256+
Replace *Module // replaced by this module
257+
Time *time.Time // time version was created
258+
Main bool // is this the main module?
259+
Indirect bool // is this module only an indirect dependency of main module?
260+
Dir string // directory holding files for this module, if any
261+
GoMod string // path to go.mod file used when loading this module, if any
262+
GoVersion string // go version used in module (e.g. "go1.22.0")
263+
Error *ModuleError // error loading module
264+
}
265+
266+
// ModuleError holds errors loading a module.
267+
type ModuleError struct {
268+
Err string // the error itself
256269
}

go/analysis/checker/checker.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,7 @@ func (act *Action) execOnce() {
311311

312312
module := &analysis.Module{} // possibly empty (non nil) in go/analysis drivers.
313313
if mod := act.Package.Module; mod != nil {
314-
module.Path = mod.Path
315-
module.Version = mod.Version
316-
module.GoVersion = mod.GoVersion
314+
module = analysisModuleFromPackagesModule(mod)
317315
}
318316

319317
// Run the analysis.
@@ -627,3 +625,29 @@ func forEach(roots []*Action, f func(*Action) error) error {
627625
}
628626
return visitAll(roots)
629627
}
628+
629+
func analysisModuleFromPackagesModule(mod *packages.Module) *analysis.Module {
630+
if mod == nil {
631+
return nil
632+
}
633+
634+
var modErr *analysis.ModuleError
635+
if mod.Error != nil {
636+
modErr = &analysis.ModuleError{
637+
Err: mod.Error.Err,
638+
}
639+
}
640+
641+
return &analysis.Module{
642+
Path: mod.Path,
643+
Version: mod.Version,
644+
Replace: analysisModuleFromPackagesModule(mod.Replace),
645+
Time: mod.Time,
646+
Main: mod.Main,
647+
Indirect: mod.Indirect,
648+
Dir: mod.Dir,
649+
GoMod: mod.GoMod,
650+
GoVersion: mod.GoVersion,
651+
Error: modErr,
652+
}
653+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2026 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package checker_test
6+
7+
import (
8+
"os"
9+
"path/filepath"
10+
"testing"
11+
12+
"golang.org/x/tools/go/analysis"
13+
"golang.org/x/tools/go/analysis/checker"
14+
"golang.org/x/tools/go/packages"
15+
"golang.org/x/tools/txtar"
16+
)
17+
18+
// TestPassModule checks that an analyzer observes the correct Pass.Module
19+
// fields (GoMod, Dir, Path, Main, GoVersion) when run on a module.
20+
func TestPassModule(t *testing.T) {
21+
const src = `
22+
-- go.mod --
23+
module example.com/hello
24+
go 1.13
25+
26+
-- hello.go --
27+
package hello
28+
`
29+
dir, err := os.MkdirTemp("", "")
30+
if err != nil {
31+
t.Fatal(err)
32+
}
33+
defer os.RemoveAll(dir)
34+
35+
fs, err := txtar.FS(txtar.Parse([]byte(src)))
36+
if err != nil {
37+
t.Fatal(err)
38+
}
39+
if err := os.CopyFS(dir, fs); err != nil {
40+
t.Fatal(err)
41+
}
42+
43+
// Capture the Module seen by the analyzer.
44+
var got *analysis.Module
45+
testAnalyzer := &analysis.Analyzer{
46+
Name: "testmodule",
47+
Doc: "Captures Pass.Module for testing.",
48+
Run: func(pass *analysis.Pass) (any, error) {
49+
got = pass.Module
50+
return nil, nil
51+
},
52+
}
53+
54+
cfg := &packages.Config{
55+
Mode: packages.LoadAllSyntax | packages.NeedModule,
56+
Dir: dir,
57+
}
58+
pkgs, err := packages.Load(cfg, "example.com/hello")
59+
if err != nil {
60+
t.Fatal(err)
61+
}
62+
63+
if _, err := checker.Analyze([]*analysis.Analyzer{testAnalyzer}, pkgs, nil); err != nil {
64+
t.Fatal(err)
65+
}
66+
67+
if got == nil {
68+
t.Fatal("Pass.Module is nil")
69+
}
70+
if got.Path != "example.com/hello" {
71+
t.Errorf("Pass.Module.Path = %q, want %q", got.Path, "example.com/hello")
72+
}
73+
if !got.Main {
74+
t.Errorf("Pass.Module.Main = false, want true")
75+
}
76+
wantGoMod := filepath.Join(dir, "go.mod")
77+
if got.GoMod != wantGoMod {
78+
t.Errorf("Pass.Module.GoMod = %q, want %q", got.GoMod, wantGoMod)
79+
}
80+
if got.Dir != dir {
81+
t.Errorf("Pass.Module.Dir = %q, want %q", got.Dir, dir)
82+
}
83+
if got.GoVersion != "1.13" {
84+
t.Errorf("Pass.Module.GoVersion = %q, want %q", got.GoVersion, "1.13")
85+
}
86+
}

0 commit comments

Comments
 (0)