aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/golangci/modinfo/module.go
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2024-09-10 12:16:33 +0200
committerTaras Madan <tarasmadan@google.com>2024-09-10 14:05:26 +0000
commitc97c816133b42257d0bcf1ee4bd178bb2a7a2b9e (patch)
tree0bcbc2e540bbf8f62f6c17887cdd53b8c2cee637 /vendor/github.com/golangci/modinfo/module.go
parent54e657429ab892ad06c90cd7c1a4eb33ba93a3dc (diff)
vendor: update
Diffstat (limited to 'vendor/github.com/golangci/modinfo/module.go')
-rw-r--r--vendor/github.com/golangci/modinfo/module.go157
1 files changed, 157 insertions, 0 deletions
diff --git a/vendor/github.com/golangci/modinfo/module.go b/vendor/github.com/golangci/modinfo/module.go
new file mode 100644
index 000000000..ff0b21b9b
--- /dev/null
+++ b/vendor/github.com/golangci/modinfo/module.go
@@ -0,0 +1,157 @@
+package modinfo
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "reflect"
+ "sort"
+ "strings"
+ "sync"
+
+ "golang.org/x/mod/modfile"
+ "golang.org/x/tools/go/analysis"
+)
+
+type ModInfo struct {
+ Path string `json:"Path"`
+ Dir string `json:"Dir"`
+ GoMod string `json:"GoMod"`
+ GoVersion string `json:"GoVersion"`
+ Main bool `json:"Main"`
+}
+
+var (
+ once sync.Once
+ information []ModInfo
+ errInfo error
+)
+
+var Analyzer = &analysis.Analyzer{
+ Name: "modinfo",
+ Doc: "Module information",
+ URL: "https://github.com/golangci/modinfo",
+ Run: runOnce,
+ ResultType: reflect.TypeOf([]ModInfo(nil)),
+}
+
+func runOnce(pass *analysis.Pass) (any, error) {
+ _, ok := os.LookupEnv("MODINFO_DEBUG_DISABLE_ONCE")
+ if ok {
+ return GetModuleInfo(pass)
+ }
+
+ once.Do(func() {
+ information, errInfo = GetModuleInfo(pass)
+ })
+
+ return information, errInfo
+}
+
+// GetModuleInfo gets modules information.
+// Always returns 1 element except for workspace (returns all the modules of the workspace).
+// Based on `go list -m -json` behavior.
+func GetModuleInfo(pass *analysis.Pass) ([]ModInfo, error) {
+ // https://github.com/golang/go/issues/44753#issuecomment-790089020
+ cmd := exec.Command("go", "list", "-m", "-json")
+ for _, file := range pass.Files {
+ name := pass.Fset.File(file.Pos()).Name()
+ if filepath.Ext(name) != ".go" {
+ continue
+ }
+
+ cmd.Dir = filepath.Dir(name)
+ break
+ }
+
+ out, err := cmd.Output()
+ if err != nil {
+ return nil, fmt.Errorf("command go list: %w: %s", err, string(out))
+ }
+
+ var infos []ModInfo
+
+ for dec := json.NewDecoder(bytes.NewBuffer(out)); dec.More(); {
+ var v ModInfo
+ if err := dec.Decode(&v); err != nil {
+ return nil, fmt.Errorf("unmarshaling error: %w: %s", err, string(out))
+ }
+
+ if v.GoMod == "" {
+ return nil, errors.New("working directory is not part of a module")
+ }
+
+ if !v.Main || v.Dir == "" {
+ continue
+ }
+
+ infos = append(infos, v)
+ }
+
+ if len(infos) == 0 {
+ return nil, errors.New("go.mod file not found")
+ }
+
+ sort.Slice(infos, func(i, j int) bool {
+ return len(infos[i].Path) > len(infos[j].Path)
+ })
+
+ return infos, nil
+}
+
+// FindModuleFromPass finds the module related to the files of the pass.
+func FindModuleFromPass(pass *analysis.Pass) (ModInfo, error) {
+ infos, ok := pass.ResultOf[Analyzer].([]ModInfo)
+ if !ok {
+ return ModInfo{}, errors.New("no modinfo analyzer result")
+ }
+
+ var name string
+ for _, file := range pass.Files {
+ f := pass.Fset.File(file.Pos()).Name()
+ if filepath.Ext(f) != ".go" {
+ continue
+ }
+
+ name = f
+ break
+ }
+
+ // no Go file found in analysis pass
+ if name == "" {
+ name, _ = os.Getwd()
+ }
+
+ for _, info := range infos {
+ if !strings.HasPrefix(name, info.Dir) {
+ continue
+ }
+ return info, nil
+ }
+
+ return ModInfo{}, errors.New("module information not found")
+}
+
+// ReadModuleFileFromPass read the `go.mod` file from the pass result.
+func ReadModuleFileFromPass(pass *analysis.Pass) (*modfile.File, error) {
+ info, err := FindModuleFromPass(pass)
+ if err != nil {
+ return nil, err
+ }
+
+ return ReadModuleFile(info)
+}
+
+// ReadModuleFile read the `go.mod` file.
+func ReadModuleFile(info ModInfo) (*modfile.File, error) {
+ raw, err := os.ReadFile(info.GoMod)
+ if err != nil {
+ return nil, fmt.Errorf("reading go.mod file: %w", err)
+ }
+
+ return modfile.Parse("go.mod", raw, nil)
+}