Skip to content

Commit ead76ab

Browse files
committed
go/analysis: Add modules to Pass
Adds optional Module information to Pass. Module contains the go module information for the package of the Pass. It contains the module's path, the module version, and the GoVersion of the module. Updates packages.PrintErrors to additionally print module errors. Fixes golang/go#66315 Change-Id: I7005b8e2f6290f16416c2438af0e6de2940ba9fc Reviewed-on: https://go-review.googlesource.com/c/tools/+/577996 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent 55d718e commit ead76ab

File tree

6 files changed

+98
-1
lines changed

6 files changed

+98
-1
lines changed

go/analysis/analysis.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ type Pass struct {
100100
TypesSizes types.Sizes // function for computing sizes of types
101101
TypeErrors []types.Error // type errors (only if Analyzer.RunDespiteErrors)
102102

103+
Module *Module // the package's enclosing module (possibly nil in some drivers)
104+
103105
// Report reports a Diagnostic, a finding about a specific location
104106
// in the analyzed source code such as a potential mistake.
105107
// It may be called by the Run function.
@@ -238,3 +240,10 @@ func (pass *Pass) String() string {
238240
type Fact interface {
239241
AFact() // dummy method to avoid type errors
240242
}
243+
244+
// A Module describes the module to which a package belongs.
245+
type Module struct {
246+
Path string // module path
247+
Version string // module version ("" if unknown, such as for workspace modules)
248+
GoVersion string // go version used in module (e.g. "go1.22.0")
249+
}

go/analysis/analysistest/analysistest_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import (
1717
"golang.org/x/tools/go/analysis/analysistest"
1818
"golang.org/x/tools/go/analysis/passes/findcall"
1919
"golang.org/x/tools/internal/testenv"
20+
"golang.org/x/tools/internal/testfiles"
21+
"golang.org/x/tools/txtar"
2022
)
2123

2224
func init() {
@@ -202,6 +204,62 @@ func F() {} // want "say hello"`,
202204
analysistest.RunWithSuggestedFixes(t, dir, noend, "a")
203205
}
204206

207+
func TestModule(t *testing.T) {
208+
const content = `
209+
Test that analysis.pass.Module is populated.
210+
211+
-- go.mod --
212+
module golang.org/fake/mod
213+
214+
go 1.21
215+
216+
require golang.org/xyz/fake v0.12.34
217+
218+
-- mod.go --
219+
// We expect a module.Path and a module.GoVersion, but an empty module.Version.
220+
221+
package mod // want "golang.org/fake/mod,,1.21"
222+
223+
import "golang.org/xyz/fake/ver"
224+
225+
var _ ver.T
226+
227+
-- vendor/modules.txt --
228+
# golang.org/xyz/fake v0.12.34
229+
## explicit; go 1.18
230+
golang.org/xyz/fake/ver
231+
232+
-- vendor/golang.org/xyz/fake/ver/ver.go --
233+
// This package is vendored so that we can populate a non-empty
234+
// Pass.Module.Version is in a test.
235+
236+
package ver //want "golang.org/xyz/fake,v0.12.34,1.18"
237+
238+
type T string
239+
`
240+
fs, err := txtar.FS(txtar.Parse([]byte(content)))
241+
if err != nil {
242+
t.Fatal(err)
243+
}
244+
dir := testfiles.CopyToTmp(t, fs)
245+
246+
filever := &analysis.Analyzer{
247+
Name: "mod",
248+
Doc: "reports module information",
249+
Run: func(pass *analysis.Pass) (any, error) {
250+
msg := "no module info"
251+
if m := pass.Module; m != nil {
252+
msg = fmt.Sprintf("%s,%s,%s", m.Path, m.Version, m.GoVersion)
253+
}
254+
for _, file := range pass.Files {
255+
pass.Reportf(file.Package, "%s", msg)
256+
}
257+
return nil, nil
258+
},
259+
}
260+
analysistest.Run(t, dir, filever, "golang.org/fake/mod", "golang.org/xyz/fake/ver")
261+
}
262+
205263
type errorfunc func(string)
206264

207265
func (f errorfunc) Errorf(format string, args ...interface{}) {

go/analysis/internal/checker/checker.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func Run(args []string, analyzers []*analysis.Analyzer) (exitcode int) {
138138
}
139139

140140
pkgsExitCode := 0
141-
// Print package errors regardless of RunDespiteErrors.
141+
// Print package and module errors regardless of RunDespiteErrors.
142142
// Do not exit if there are errors, yet.
143143
if n := packages.PrintErrors(initial); n > 0 {
144144
pkgsExitCode = 1
@@ -720,6 +720,13 @@ func (act *action) execOnce() {
720720
}
721721
}
722722

723+
module := &analysis.Module{} // possibly empty (non nil) in go/analysis drivers.
724+
if mod := act.pkg.Module; mod != nil {
725+
module.Path = mod.Path
726+
module.Version = mod.Version
727+
module.GoVersion = mod.GoVersion
728+
}
729+
723730
// Run the analysis.
724731
pass := &analysis.Pass{
725732
Analyzer: act.a,
@@ -731,6 +738,7 @@ func (act *action) execOnce() {
731738
TypesInfo: act.pkg.TypesInfo,
732739
TypesSizes: act.pkg.TypesSizes,
733740
TypeErrors: act.pkg.TypeErrors,
741+
Module: module,
734742

735743
ResultOf: inputs,
736744
Report: func(d analysis.Diagnostic) { act.diagnostics = append(act.diagnostics, d) },

go/analysis/unitchecker/separate_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"io"
1616
"os"
1717
"path/filepath"
18+
"sort"
1819
"strings"
1920
"sync/atomic"
2021
"testing"
@@ -167,6 +168,8 @@ func MyPrintf(format string, args ...any) {
167168
if v := pkg.Module.GoVersion; v != "" {
168169
cfg.GoVersion = "go" + v
169170
}
171+
cfg.ModulePath = pkg.Module.Path
172+
cfg.ModuleVersion = pkg.Module.Version
170173
}
171174

172175
// Write the JSON configuration message to a file.
@@ -220,6 +223,7 @@ func MyPrintf(format string, args ...any) {
220223
// from separate analysis of "main", "lib", and "fmt":
221224

222225
const want = `/main/main.go:6:2: [printf] separate/lib.MyPrintf format %s has arg 123 of wrong type int`
226+
sort.Strings(allDiagnostics)
223227
if got := strings.Join(allDiagnostics, "\n"); got != want {
224228
t.Errorf("Got: %s\nWant: %s", got, want)
225229
}

go/analysis/unitchecker/unitchecker.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ type Config struct {
6666
GoFiles []string
6767
NonGoFiles []string
6868
IgnoredFiles []string
69+
ModulePath string // module path
70+
ModuleVersion string // module version
6971
ImportMap map[string]string // maps import path to package path
7072
PackageFile map[string]string // maps package path to file of type information
7173
Standard map[string]bool // package belongs to standard library
@@ -359,6 +361,12 @@ func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]re
359361
factFilter[reflect.TypeOf(f)] = true
360362
}
361363

364+
module := &analysis.Module{
365+
Path: cfg.ModulePath,
366+
Version: cfg.ModuleVersion,
367+
GoVersion: cfg.GoVersion,
368+
}
369+
362370
pass := &analysis.Pass{
363371
Analyzer: a,
364372
Fset: fset,
@@ -377,6 +385,7 @@ func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]re
377385
ImportPackageFact: facts.ImportPackageFact,
378386
ExportPackageFact: facts.ExportPackageFact,
379387
AllPackageFacts: func() []analysis.PackageFact { return facts.AllPackageFacts(factFilter) },
388+
Module: module,
380389
}
381390
pass.ReadFile = analysisinternal.MakeReadFile(pass)
382391

go/packages/visit.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,20 @@ func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) {
4949
// PrintErrors returns the number of errors printed.
5050
func PrintErrors(pkgs []*Package) int {
5151
var n int
52+
errModules := make(map[*Module]bool)
5253
Visit(pkgs, nil, func(pkg *Package) {
5354
for _, err := range pkg.Errors {
5455
fmt.Fprintln(os.Stderr, err)
5556
n++
5657
}
58+
59+
// Print pkg.Module.Error once if present.
60+
mod := pkg.Module
61+
if mod != nil && mod.Error != nil && !errModules[mod] {
62+
errModules[mod] = true
63+
fmt.Fprintln(os.Stderr, mod.Error.Err)
64+
n++
65+
}
5766
})
5867
return n
5968
}

0 commit comments

Comments
 (0)