From d2e63b9ad8e84f35df7e00bb725f8b89179e9074 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Thu, 18 May 2023 18:36:52 +0200 Subject: [PATCH 01/11] feat(cmd/gno): add support for gno.mod projects in gno doc --- gnovm/cmd/gno/doc.go | 46 ++++++++++++- gnovm/pkg/doc/dirs.go | 136 +++++++++++++++++++++++++++++++------ gnovm/pkg/doc/doc.go | 10 ++- gnovm/pkg/gnomod/file.go | 27 +++++--- gnovm/pkg/gnomod/gnomod.go | 23 +++++-- 5 files changed, 204 insertions(+), 38 deletions(-) diff --git a/gnovm/cmd/gno/doc.go b/gnovm/cmd/gno/doc.go index c54e289cd67..fd0e70d1879 100644 --- a/gnovm/cmd/gno/doc.go +++ b/gnovm/cmd/gno/doc.go @@ -2,7 +2,10 @@ package main import ( "context" + "errors" "flag" + "fmt" + "os" "path/filepath" "github.com/gnolang/gno/gnovm/pkg/doc" @@ -74,8 +77,24 @@ func execDoc(cfg *docCfg, args []string, io *commands.IO) error { if cfg.rootDir == "" { cfg.rootDir = guessRootDir() } + + wd, err := os.Getwd() + if err != nil { + return fmt.Errorf("could not determine working directory: %w", err) + } + + rd, err := findRootDir(wd) + if err != nil && !errors.Is(err, errGnoModNotFound) { + return fmt.Errorf("error determining root gno.mod file: %w", err) + } + var modDirs []string + if rd != "" { + modDirs = append(modDirs, rd) + } + + // select dirs from which to gather directories dirs := []string{filepath.Join(cfg.rootDir, "gnovm/stdlibs"), filepath.Join(cfg.rootDir, "examples")} - res, err := doc.ResolveDocumentable(dirs, args, cfg.unexported) + res, err := doc.ResolveDocumentable(dirs, modDirs, args, cfg.unexported) if res == nil { return err } @@ -92,3 +111,28 @@ func execDoc(cfg *docCfg, args []string, io *commands.IO) error { }, ) } + +var errGnoModNotFound = errors.New("gno.mod file not found in current or any parent directory") + +// XXX: implemented by harry in #799, which at time of writing isn't merged yet. +func findRootDir(absPath string) (string, error) { + if !filepath.IsAbs(absPath) { + return "", errors.New("requires absolute path") + } + + root := filepath.VolumeName(absPath) + string(filepath.Separator) + for absPath != root { + modPath := filepath.Join(absPath, "gno.mod") + _, err := os.Stat(modPath) + if errors.Is(err, os.ErrNotExist) { + absPath = filepath.Dir(absPath) + continue + } + if err != nil { + return "", err + } + return absPath, nil + } + + return "", errGnoModNotFound +} diff --git a/gnovm/pkg/doc/dirs.go b/gnovm/pkg/doc/dirs.go index c6f90d167e4..ec14b3a4fe5 100644 --- a/gnovm/pkg/doc/dirs.go +++ b/gnovm/pkg/doc/dirs.go @@ -7,9 +7,12 @@ package doc import ( "log" "os" + "path" "path/filepath" "sort" "strings" + + "github.com/gnolang/gno/gnovm/pkg/gnomod" ) // A bfsDir describes a directory holding code by specifying @@ -30,15 +33,85 @@ type bfsDirs struct { } // newDirs begins scanning the given stdlibs directory. -func newDirs(dirs ...string) *bfsDirs { +// dirs are "gopath-like" directories, such as @/gnovm/stdlibs and @/examples. +// modDirs are user directories, expected to have gno.mod files +func newDirs(dirs []string, modDirs []string) *bfsDirs { d := &bfsDirs{ hist: make([]bfsDir, 0, 256), scan: make(chan bfsDir), } - go d.walk(dirs) + + roots := make([]bfsDir, 0, len(dirs)+len(modDirs)) + for _, dir := range dirs { + roots = append(roots, bfsDir{ + dir: dir, + importPath: "", + }) + } + + for _, mdir := range modDirs { + gm, exists := tryParseGnoMod(filepath.Join(mdir, "gno.mod")) + if gm == nil && exists { + // gno.mod could not be parsed but exists. + // as this would lead us to not having correct import paths (as we can't + // parse the module name from gno.mod), skip this directory; the user has + // received a console warning regardless from tryParseGnoMod. + continue + } + roots = append(roots, bfsDir{ + dir: mdir, + importPath: gm.Module.Mod.Path, + }) + roots = append(roots, getGnoModDirs(gm)...) + } + + go d.walk(roots) return d } +// tries to parse gno mod file. +// second return parameter is whether gno.mod exists. +func tryParseGnoMod(fname string) (*gnomod.File, bool) { + file, err := os.Stat(fname) + // early exit for errors and non-file fname + if err != nil || file.IsDir() { + if err != nil && !os.IsNotExist(err) { + log.Printf("could not read go.mod file at %q: %v", fname, err) + } + return nil, !os.IsNotExist(err) + } + + b, err := os.ReadFile(fname) + if err != nil { + log.Printf("could not read go.mod file at %q: %v", fname, err) + return nil, true + } + gm, err := gnomod.Parse(fname, b) + if err == nil { + err = gm.Validate() + } + if err != nil { + log.Printf("could not parse go.mod file at %q: %v", fname, err) + } + return gm, true +} + +func getGnoModDirs(gm *gnomod.File) []bfsDir { + // cmd/go makes use of the go list command, we don't have that here. + + dirs := make([]bfsDir, 0, len(gm.Require)) + for _, r := range gm.Require { + mv := gm.Resolve(r) + path := gnomod.PackageDir("", mv) + dirs = append(dirs, bfsDir{ + importPath: mv.Path, + dir: path, + }) + } + + return dirs +} + // Reset puts the scan back at the beginning. func (d *bfsDirs) Reset() { d.offset = 0 @@ -62,37 +135,50 @@ func (d *bfsDirs) Next() (bfsDir, bool) { } // walk walks the trees in the given roots. -func (d *bfsDirs) walk(roots []string) { +func (d *bfsDirs) walk(roots []bfsDir) { for _, root := range roots { d.bfsWalkRoot(root) } close(d.scan) } +type bfsRootDir struct { + root string + pkgPrefix string +} + // bfsWalkRoot walks a single directory hierarchy in breadth-first lexical order. // Each Go source directory it finds is delivered on d.scan. -func (d *bfsDirs) bfsWalkRoot(root string) { - root = filepath.Clean(root) +func (d *bfsDirs) bfsWalkRoot(root bfsDir) { + root.dir = filepath.Clean(root.dir) // this is the queue of directories to examine in this pass. - this := []string{} + this := []bfsDir{} // next is the queue of directories to examine in the next pass. - next := []string{root} + next := []bfsDir{root} for len(next) > 0 { this, next = next, this[:0] for _, dir := range this { - fd, err := os.Open(dir) + fd, err := os.Open(dir.dir) if err != nil { log.Print(err) continue } - entries, err := fd.Readdir(0) + + // read dir entries. + entries, err := fd.ReadDir(0) fd.Close() if err != nil { log.Print(err) continue } + + // stop at module boundaries + if dir.dir != root.dir && containsGnoMod(entries) { + continue + } + hasGnoFiles := false for _, entry := range entries { name := entry.Name() @@ -111,20 +197,28 @@ func (d *bfsDirs) bfsWalkRoot(root string) { continue } // Remember this (fully qualified) directory for the next pass. - next = append(next, filepath.Join(dir, name)) + next = append(next, bfsDir{ + dir: filepath.Join(dir.dir, name), + importPath: path.Join(dir.importPath, name), + }) } if hasGnoFiles { // It's a candidate. - var importPath string - if len(dir) > len(root) { - importPath = filepath.ToSlash(dir[len(root)+1:]) - } - d.scan <- bfsDir{importPath, dir} + d.scan <- dir } } } } +func containsGnoMod(entries []os.DirEntry) bool { + for _, entry := range entries { + if entry.Name() == "gno.mod" && !entry.IsDir() { + return true + } + } + return false +} + // findPackage finds a package iterating over d where the import path has // name as a suffix (which may be a package name or a fully-qualified path). // returns a list of possible directories. If a directory's import path matched @@ -139,12 +233,14 @@ func (d *bfsDirs) findPackage(name string) []bfsDir { } } sort.Slice(candidates, func(i, j int) bool { - // prefer exact matches with name - if candidates[i].importPath == name { - return true - } else if candidates[j].importPath == name { - return false + // prefer shorter paths -- if we have an exact match it will be of the + // shortest possible pkg path. + ci := strings.Count(candidates[i].importPath, "/") + cj := strings.Count(candidates[j].importPath, "/") + if ci != cj { + return ci < cj } + // use alphabetical ordering otherwise. return candidates[i].importPath < candidates[j].importPath }) return candidates diff --git a/gnovm/pkg/doc/doc.go b/gnovm/pkg/doc/doc.go index cecd97f53d9..c77ded6db7f 100644 --- a/gnovm/pkg/doc/doc.go +++ b/gnovm/pkg/doc/doc.go @@ -159,8 +159,12 @@ var fpAbs = filepath.Abs // the same as the go doc command). // An error may be returned even if documentation was resolved in case some // packages in dirs could not be parsed correctly. -func ResolveDocumentable(dirs []string, args []string, unexported bool) (Documentable, error) { - d := newDirs(dirs...) +// +// dirs specifies the gno system directories to scan which specify full import paths +// in their directories, such as @/examples and @/gnovm/stdlibs; modDirs specifies +// directories which contain a gno.mod file. +func ResolveDocumentable(dirs, modDirs, args []string, unexported bool) (Documentable, error) { + d := newDirs(dirs, modDirs) parsed, ok := parseArgs(args) if !ok { @@ -197,7 +201,7 @@ func resolveDocumentable(dirs *bfsDirs, parsed docArgs, unexported bool) (Docume // there are no candidates. // if this is ambiguous, remove ambiguity and try parsing args using pkg as the symbol. if !parsed.pkgAmbiguous { - return nil, fmt.Errorf("commands/doc: package not found: %q (note: local packages are not yet supported)", parsed.pkg) + return nil, fmt.Errorf("commands/doc: package not found: %q", parsed.pkg) } parsed = docArgs{pkg: ".", sym: parsed.pkg, acc: parsed.sym} return resolveDocumentable(dirs, parsed, unexported) diff --git a/gnovm/pkg/gnomod/file.go b/gnovm/pkg/gnomod/file.go index 473e60850ca..43d6560cbc7 100644 --- a/gnovm/pkg/gnomod/file.go +++ b/gnovm/pkg/gnomod/file.go @@ -33,33 +33,42 @@ func (f *File) Validate() error { return nil } +// Resolve takes a Require directive from File and returns any adequate replacement +// following the Replace directives. +func (f *File) Resolve(r *modfile.Require) module.Version { + mod, replaced := isReplaced(r.Mod, f.Replace) + if replaced { + return mod + } + return f.Module.Mod +} + // FetchDeps fetches and writes gno.mod packages // in GOPATH/pkg/gnomod/ func (f *File) FetchDeps(path string, remote string, verbose bool) error { for _, r := range f.Require { - mod, replaced := isReplaced(r.Mod, f.Replace) - if replaced { + mod := f.Resolve(r) + if r.Mod.Path != mod.Path { if modfile.IsDirectoryPath(mod.Path) { continue } - r.Mod = *mod } indirect := "" if r.Indirect { indirect = "// indirect" } - _, err := os.Stat(filepath.Join(path, r.Mod.Path)) + _, err := os.Stat(PackageDir(path, mod)) if !os.IsNotExist(err) { if verbose { - log.Println("cached", r.Mod.Path, indirect) + log.Println("cached", mod.Path, indirect) } continue } if verbose { - log.Println("fetching", r.Mod.Path, indirect) + log.Println("fetching", mod.Path, indirect) } - requirements, err := writePackage(remote, path, r.Mod.Path) + requirements, err := writePackage(remote, path, mod.Path) if err != nil { return fmt.Errorf("writepackage: %w", err) } @@ -67,7 +76,7 @@ func (f *File) FetchDeps(path string, remote string, verbose bool) error { modFile := &File{ Module: &modfile.Module{ Mod: module.Version{ - Path: r.Mod.Path, + Path: mod.Path, }, }, } @@ -101,7 +110,7 @@ func (f *File) FetchDeps(path string, remote string, verbose bool) error { if err != nil { return err } - err = goMod.WriteToPath(filepath.Join(path, r.Mod.Path)) + err = goMod.WriteToPath(PackageDir(path, mod)) if err != nil { return err } diff --git a/gnovm/pkg/gnomod/gnomod.go b/gnovm/pkg/gnomod/gnomod.go index c198adc1ca8..0fd22d659a3 100644 --- a/gnovm/pkg/gnomod/gnomod.go +++ b/gnovm/pkg/gnomod/gnomod.go @@ -20,6 +20,19 @@ func GetGnoModPath() string { return filepath.Join(client.HomeDir(), "pkg", "mod") } +// PackageDir resolves a given module.Version to the path on the filesystem. +// If root is dir, it is defaulted to the value of [GetGnoModPath]. +func PackageDir(root string, v module.Version) string { + // This is also used internally exactly like filepath.Join; but we'll keep + // the calls centralized to make sure we can change the path centrally should + // we start including the module version in the path. + + if root == "" { + root = GetGnoModPath() + } + return filepath.Join(root, v.Path) +} + func writePackage(remote, basePath, pkgPath string) (requirements []string, err error) { res, err := queryChain(remote, queryPathFile, []byte(pkgPath)) if err != nil { @@ -150,15 +163,15 @@ func GnoToGoMod(f File) (*File, error) { return &f, nil } -func isReplaced(module module.Version, repl []*modfile.Replace) (*module.Version, bool) { +func isReplaced(mod module.Version, repl []*modfile.Replace) (module.Version, bool) { for _, r := range repl { - hasNoVersion := r.Old.Path == module.Path && r.Old.Version == "" - hasExactVersion := r.Old == module + hasNoVersion := r.Old.Path == mod.Path && r.Old.Version == "" + hasExactVersion := r.Old == mod if hasNoVersion || hasExactVersion { - return &r.New, true + return r.New, true } } - return nil, false + return module.Version{}, false } func removeDuplicateStr(str []string) (res []string) { From 4d16fe925e6ba8e3a1cffb085fafb5d55c6e15d1 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Thu, 18 May 2023 18:39:53 +0200 Subject: [PATCH 02/11] fix tests --- gnovm/pkg/doc/dirs_test.go | 2 +- gnovm/pkg/doc/doc_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gnovm/pkg/doc/dirs_test.go b/gnovm/pkg/doc/dirs_test.go index a7c4926a8c8..50b10ae4a29 100644 --- a/gnovm/pkg/doc/dirs_test.go +++ b/gnovm/pkg/doc/dirs_test.go @@ -13,7 +13,7 @@ func tNewDirs(t *testing.T) (string, *bfsDirs) { t.Helper() p, err := filepath.Abs("./testdata/dirs") require.NoError(t, err) - return p, newDirs(p) + return p, newDirs([]string{p}, nil) } func TestDirs_findPackage(t *testing.T) { diff --git a/gnovm/pkg/doc/doc_test.go b/gnovm/pkg/doc/doc_test.go index 1cccb4106f7..d4abfa04e30 100644 --- a/gnovm/pkg/doc/doc_test.go +++ b/gnovm/pkg/doc/doc_test.go @@ -15,7 +15,7 @@ func TestResolveDocumentable(t *testing.T) { p, err := os.Getwd() require.NoError(t, err) path := func(s string) string { return filepath.Join(p, "testdata/integ", s) } - dirs := newDirs(path("")) + dirs := newDirs([]string{path("")}, nil) getDir := func(p string) bfsDir { return dirs.findDir(path(p))[0] } pdata := func(p string, unexp bool) *pkgData { pd, err := newPkgData(getDir(p), unexp) @@ -93,8 +93,8 @@ func TestResolveDocumentable(t *testing.T) { {"errInvalidArgs", []string{"1", "2", "3"}, false, nil, "invalid arguments: [1 2 3]"}, {"errNoCandidates", []string{"math", "Big"}, false, nil, `package not found: "math"`}, - {"errNoCandidates2", []string{"LocalSymbol"}, false, nil, `local packages are not yet supported`}, - {"errNoCandidates3", []string{"Symbol.Accessible"}, false, nil, `local packages are not yet supported`}, + {"errNoCandidates2", []string{"LocalSymbol"}, false, nil, `package not found`}, + {"errNoCandidates3", []string{"Symbol.Accessible"}, false, nil, `package not found`}, {"errNonExisting", []string{"rand.NotExisting"}, false, nil, `could not resolve arguments`}, {"errUnexp", []string{"crypto/rand.unexp"}, false, nil, "could not resolve arguments"}, {"errDirNotapkg", []string{"./test_notapkg"}, false, nil, `package not found: "./test_notapkg"`}, @@ -110,7 +110,7 @@ func TestResolveDocumentable(t *testing.T) { fpAbs = func(s string) (string, error) { return filepath.Clean(filepath.Join(path("wd"), s)), nil } defer func() { fpAbs = filepath.Abs }() } - result, err := ResolveDocumentable([]string{path("")}, tc.args, tc.unexp) + result, err := ResolveDocumentable([]string{path("")}, nil, tc.args, tc.unexp) // we use stripFset because d.pkgData.fset contains sync/atomic values, // which in turn makes reflect.DeepEqual compare the two sync.Atomic values. assert.Equal(t, stripFset(tc.expect), stripFset(result), "documentables should match") From f1220b081809e9950f81d698f6e8cc8f330205a6 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Thu, 18 May 2023 18:53:39 +0200 Subject: [PATCH 03/11] remove bfsRootDir --- gnovm/pkg/doc/dirs.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gnovm/pkg/doc/dirs.go b/gnovm/pkg/doc/dirs.go index ec14b3a4fe5..fff86907248 100644 --- a/gnovm/pkg/doc/dirs.go +++ b/gnovm/pkg/doc/dirs.go @@ -142,11 +142,6 @@ func (d *bfsDirs) walk(roots []bfsDir) { close(d.scan) } -type bfsRootDir struct { - root string - pkgPrefix string -} - // bfsWalkRoot walks a single directory hierarchy in breadth-first lexical order. // Each Go source directory it finds is delivered on d.scan. func (d *bfsDirs) bfsWalkRoot(root bfsDir) { From 36b956c76746bfecd6c52269a197c4f60e2e82b1 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Fri, 19 May 2023 16:42:05 +0200 Subject: [PATCH 04/11] update compat doc --- gnovm/docs/go-gno-compatibility.md | 48 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/gnovm/docs/go-gno-compatibility.md b/gnovm/docs/go-gno-compatibility.md index 6d83f54cbfa..016065d3438 100644 --- a/gnovm/docs/go-gno-compatibility.md +++ b/gnovm/docs/go-gno-compatibility.md @@ -319,27 +319,27 @@ Legend: full, partial, missing, TBD. ## Tooling (`gno` binary) -| go command | gno command | comment | -|-------------------|------------------|-----------------------------------------------| -| go bug | | see https://github.com/gnolang/gno/issues/733 | -| go build | gno build | same intention, limited compatibility | -| go clean | | | -| go doc | | see https://github.com/gnolang/gno/pull/610 | -| go env | | | -| go fix | | | -| go fmt | | | -| go generate | | | -| go get | | | -| go help | | | -| go install | | | -| go list | | | -| go mod | | | -| + go mod download | gno mod download | same behavior | -| | gno precompile | | -| go work | | | -| | gno repl | | -| go run | gno run | | -| go test | gno test | limited compatibility | -| go tool | | | -| go version | | | -| go vet | | | +| go command | gno command | comment | +|-------------------|------------------|-----------------------------------------------------------------------| +| go bug | | see https://github.com/gnolang/gno/issues/733 | +| go build | gno build | same intention, limited compatibility | +| go clean | | | +| go doc | gno doc | limited compatibility; see https://github.com/gnolang/gno/issues/522 | +| go env | | | +| go fix | | | +| go fmt | | | +| go generate | | | +| go get | | | +| go help | | | +| go install | | | +| go list | | | +| go mod | | | +| + go mod download | gno mod download | same behavior | +| | gno precompile | | +| go work | | | +| | gno repl | | +| go run | gno run | | +| go test | gno test | limited compatibility | +| go tool | | | +| go version | | | +| go vet | | | From cfd4af1ec5cda170691eb3ae604dc6bdd31bc1be Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Fri, 19 May 2023 18:32:08 +0200 Subject: [PATCH 05/11] add tests --- gnovm/pkg/doc/dirs_test.go | 54 +++++++++++++------ gnovm/pkg/doc/doc_test.go | 11 +++- .../pkg/doc/testdata/dirs/module/alpha/a.gno | 2 + gnovm/pkg/doc/testdata/dirs/module/gno.mod | 0 .../dirsdep/pkg/mod/dirs.mod/dep/a.gno | 0 .../dirsdep/pkg/mod/dirs.mod/dep/alpha/a.gno | 0 .../dirsdep/pkg/mod/dirs.mod/dep/gno.mod | 1 + gnovm/pkg/doc/testdata/dirsmod/gno.mod | 5 ++ .../pkg/doc/testdata/dirsmod/math/rand/a.gno | 0 gnovm/pkg/doc/testdata/integ/mod/gno.mod | 1 + gnovm/pkg/doc/testdata/integ/mod/mod.gno | 5 ++ .../pkg/doc/testdata/integ/modignored/gno.mod | 1 + .../pkg/doc/testdata/integ/modignored/mod.gno | 5 ++ gnovm/pkg/gnomod/file.go | 2 +- 14 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 gnovm/pkg/doc/testdata/dirs/module/alpha/a.gno create mode 100644 gnovm/pkg/doc/testdata/dirs/module/gno.mod create mode 100644 gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/a.gno create mode 100644 gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/alpha/a.gno create mode 100644 gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/gno.mod create mode 100644 gnovm/pkg/doc/testdata/dirsmod/gno.mod create mode 100644 gnovm/pkg/doc/testdata/dirsmod/math/rand/a.gno create mode 100644 gnovm/pkg/doc/testdata/integ/mod/gno.mod create mode 100644 gnovm/pkg/doc/testdata/integ/mod/mod.gno create mode 100644 gnovm/pkg/doc/testdata/integ/modignored/gno.mod create mode 100644 gnovm/pkg/doc/testdata/integ/modignored/mod.gno diff --git a/gnovm/pkg/doc/dirs_test.go b/gnovm/pkg/doc/dirs_test.go index 50b10ae4a29..bbf8bcad70d 100644 --- a/gnovm/pkg/doc/dirs_test.go +++ b/gnovm/pkg/doc/dirs_test.go @@ -1,6 +1,7 @@ package doc import ( + "os" "path/filepath" "strings" "testing" @@ -11,27 +12,50 @@ import ( func tNewDirs(t *testing.T) (string, *bfsDirs) { t.Helper() - p, err := filepath.Abs("./testdata/dirs") + + wd, err := os.Getwd() require.NoError(t, err) - return p, newDirs([]string{p}, nil) + + // modify GNO_HOME to testdata/dirsdep -- this allows us to test + // dependency lookup by dirs. + old, ex := os.LookupEnv("GNO_HOME") + os.Setenv("GNO_HOME", filepath.Join(wd, "testdata/dirsdep")) + t.Cleanup(func() { + if ex { + os.Setenv("GNO_HOME", old) + } else { + os.Unsetenv("GNO_HOME") + } + }) + + return filepath.Join(wd, "testdata"), + newDirs([]string{filepath.Join(wd, "testdata/dirs")}, []string{filepath.Join(wd, "testdata/dirsmod")}) } func TestDirs_findPackage(t *testing.T) { - abs, d := tNewDirs(t) + td, d := tNewDirs(t) tt := []struct { name string res []bfsDir }{ {"rand", []bfsDir{ - {importPath: "rand", dir: filepath.Join(abs, "rand")}, - {importPath: "crypto/rand", dir: filepath.Join(abs, "crypto/rand")}, - {importPath: "math/rand", dir: filepath.Join(abs, "math/rand")}, + {importPath: "rand", dir: filepath.Join(td, "dirs/rand")}, + {importPath: "crypto/rand", dir: filepath.Join(td, "dirs/crypto/rand")}, + {importPath: "math/rand", dir: filepath.Join(td, "dirs/math/rand")}, + {importPath: "dirs.mod/prefix/math/rand", dir: filepath.Join(td, "dirsmod/math/rand")}, }}, {"crypto/rand", []bfsDir{ - {importPath: "crypto/rand", dir: filepath.Join(abs, "crypto/rand")}, + {importPath: "crypto/rand", dir: filepath.Join(td, "dirs/crypto/rand")}, + }}, + {"dep", []bfsDir{ + {importPath: "dirs.mod/dep", dir: filepath.Join(td, "dirsdep/pkg/mod/dirs.mod/dep")}, + }}, + {"alpha", []bfsDir{ + {importPath: "dirs.mod/dep/alpha", dir: filepath.Join(td, "dirsdep/pkg/mod/dirs.mod/dep/alpha")}, + // no testdir/module/alpha as it is inside a module }}, {"math", []bfsDir{ - {importPath: "math", dir: filepath.Join(abs, "math")}, + {importPath: "math", dir: filepath.Join(td, "dirs/math")}, }}, {"ath", []bfsDir{}}, {"/math", []bfsDir{}}, @@ -47,21 +71,21 @@ func TestDirs_findPackage(t *testing.T) { } func TestDirs_findDir(t *testing.T) { - abs, d := tNewDirs(t) + td, d := tNewDirs(t) tt := []struct { name string in string res []bfsDir }{ - {"rand", filepath.Join(abs, "rand"), []bfsDir{ - {importPath: "rand", dir: filepath.Join(abs, "rand")}, + {"rand", filepath.Join(td, "dirs/rand"), []bfsDir{ + {importPath: "rand", dir: filepath.Join(td, "dirs/rand")}, }}, - {"crypto/rand", filepath.Join(abs, "crypto/rand"), []bfsDir{ - {importPath: "crypto/rand", dir: filepath.Join(abs, "crypto/rand")}, + {"crypto/rand", filepath.Join(td, "dirs/crypto/rand"), []bfsDir{ + {importPath: "crypto/rand", dir: filepath.Join(td, "dirs/crypto/rand")}, }}, // ignored (dir name testdata), so should not return anything. - {"crypto/testdata/rand", filepath.Join(abs, "crypto/testdata/rand"), nil}, - {"xx", filepath.Join(abs, "xx"), nil}, + {"crypto/testdata/rand", filepath.Join(td, "dirs/crypto/testdata/rand"), nil}, + {"xx", filepath.Join(td, "dirs/xx"), nil}, {"xx2", "/xx2", nil}, } for _, tc := range tt { diff --git a/gnovm/pkg/doc/doc_test.go b/gnovm/pkg/doc/doc_test.go index d4abfa04e30..8a959e142d0 100644 --- a/gnovm/pkg/doc/doc_test.go +++ b/gnovm/pkg/doc/doc_test.go @@ -15,7 +15,7 @@ func TestResolveDocumentable(t *testing.T) { p, err := os.Getwd() require.NoError(t, err) path := func(s string) string { return filepath.Join(p, "testdata/integ", s) } - dirs := newDirs([]string{path("")}, nil) + dirs := newDirs([]string{path("")}, []string{path("mod")}) getDir := func(p string) bfsDir { return dirs.findDir(path(p))[0] } pdata := func(p string, unexp bool) *pkgData { pd, err := newPkgData(getDir(p), unexp) @@ -31,7 +31,9 @@ func TestResolveDocumentable(t *testing.T) { errContains string }{ {"package", []string{"crypto/rand"}, false, &documentable{bfsDir: getDir("crypto/rand")}, ""}, + {"packageMod", []string{"gno.land/mod"}, false, &documentable{bfsDir: getDir("mod")}, ""}, {"dir", []string{"./testdata/integ/crypto/rand"}, false, &documentable{bfsDir: getDir("crypto/rand")}, ""}, + {"dirMod", []string{"./testdata/integ/mod"}, false, &documentable{bfsDir: getDir("mod")}, ""}, {"dirAbs", []string{path("crypto/rand")}, false, &documentable{bfsDir: getDir("crypto/rand")}, ""}, // test_notapkg exists in local dir and also path("test_notapkg"). // ResolveDocumentable should first try local dir, and seeing as it is not a valid dir, try searching it as a package. @@ -96,6 +98,8 @@ func TestResolveDocumentable(t *testing.T) { {"errNoCandidates2", []string{"LocalSymbol"}, false, nil, `package not found`}, {"errNoCandidates3", []string{"Symbol.Accessible"}, false, nil, `package not found`}, {"errNonExisting", []string{"rand.NotExisting"}, false, nil, `could not resolve arguments`}, + {"errIgnoredMod", []string{"modignored"}, false, nil, `package not found`}, + {"errIgnoredMod2", []string{"./testdata/integ/modignored"}, false, nil, `package not found`}, {"errUnexp", []string{"crypto/rand.unexp"}, false, nil, "could not resolve arguments"}, {"errDirNotapkg", []string{"./test_notapkg"}, false, nil, `package not found: "./test_notapkg"`}, } @@ -110,7 +114,10 @@ func TestResolveDocumentable(t *testing.T) { fpAbs = func(s string) (string, error) { return filepath.Clean(filepath.Join(path("wd"), s)), nil } defer func() { fpAbs = filepath.Abs }() } - result, err := ResolveDocumentable([]string{path("")}, nil, tc.args, tc.unexp) + result, err := ResolveDocumentable( + []string{path("")}, []string{path("mod")}, + tc.args, tc.unexp, + ) // we use stripFset because d.pkgData.fset contains sync/atomic values, // which in turn makes reflect.DeepEqual compare the two sync.Atomic values. assert.Equal(t, stripFset(tc.expect), stripFset(result), "documentables should match") diff --git a/gnovm/pkg/doc/testdata/dirs/module/alpha/a.gno b/gnovm/pkg/doc/testdata/dirs/module/alpha/a.gno new file mode 100644 index 00000000000..36373e5cc5b --- /dev/null +++ b/gnovm/pkg/doc/testdata/dirs/module/alpha/a.gno @@ -0,0 +1,2 @@ +// This directory should not be inspected, as it is inside a module +// and not explicitly added as a rootDir. diff --git a/gnovm/pkg/doc/testdata/dirs/module/gno.mod b/gnovm/pkg/doc/testdata/dirs/module/gno.mod new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/a.gno b/gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/a.gno new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/alpha/a.gno b/gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/alpha/a.gno new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/gno.mod b/gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/gno.mod new file mode 100644 index 00000000000..89b5ec87c57 --- /dev/null +++ b/gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/gno.mod @@ -0,0 +1 @@ +package dirs.mod/dep diff --git a/gnovm/pkg/doc/testdata/dirsmod/gno.mod b/gnovm/pkg/doc/testdata/dirsmod/gno.mod new file mode 100644 index 00000000000..6c8008b958c --- /dev/null +++ b/gnovm/pkg/doc/testdata/dirsmod/gno.mod @@ -0,0 +1,5 @@ +module dirs.mod/prefix + +require ( + dirs.mod/dep v0.0.0 +) diff --git a/gnovm/pkg/doc/testdata/dirsmod/math/rand/a.gno b/gnovm/pkg/doc/testdata/dirsmod/math/rand/a.gno new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gnovm/pkg/doc/testdata/integ/mod/gno.mod b/gnovm/pkg/doc/testdata/integ/mod/gno.mod new file mode 100644 index 00000000000..248c7a8010c --- /dev/null +++ b/gnovm/pkg/doc/testdata/integ/mod/gno.mod @@ -0,0 +1 @@ +module gno.land/mod diff --git a/gnovm/pkg/doc/testdata/integ/mod/mod.gno b/gnovm/pkg/doc/testdata/integ/mod/mod.gno new file mode 100644 index 00000000000..6ebcefcdcba --- /dev/null +++ b/gnovm/pkg/doc/testdata/integ/mod/mod.gno @@ -0,0 +1,5 @@ +package mod + +// ModHello greets you warmly! +func ModHello() { +} diff --git a/gnovm/pkg/doc/testdata/integ/modignored/gno.mod b/gnovm/pkg/doc/testdata/integ/modignored/gno.mod new file mode 100644 index 00000000000..d3b3c774e2e --- /dev/null +++ b/gnovm/pkg/doc/testdata/integ/modignored/gno.mod @@ -0,0 +1 @@ +module gno.land/modignored diff --git a/gnovm/pkg/doc/testdata/integ/modignored/mod.gno b/gnovm/pkg/doc/testdata/integ/modignored/mod.gno new file mode 100644 index 00000000000..6ebcefcdcba --- /dev/null +++ b/gnovm/pkg/doc/testdata/integ/modignored/mod.gno @@ -0,0 +1,5 @@ +package mod + +// ModHello greets you warmly! +func ModHello() { +} diff --git a/gnovm/pkg/gnomod/file.go b/gnovm/pkg/gnomod/file.go index 43d6560cbc7..7f8879a35ed 100644 --- a/gnovm/pkg/gnomod/file.go +++ b/gnovm/pkg/gnomod/file.go @@ -40,7 +40,7 @@ func (f *File) Resolve(r *modfile.Require) module.Version { if replaced { return mod } - return f.Module.Mod + return r.Mod } // FetchDeps fetches and writes gno.mod packages From 63b87d5a37f8033d29096decef77dcff31043d0e Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Fri, 19 May 2023 19:08:15 +0200 Subject: [PATCH 06/11] harioms pr merged, remove duplicate code --- gnovm/cmd/gno/doc.go | 30 +++--------------------------- gnovm/pkg/gnomod/utils.go | 9 ++++++++- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/gnovm/cmd/gno/doc.go b/gnovm/cmd/gno/doc.go index fd0e70d1879..51b70b57588 100644 --- a/gnovm/cmd/gno/doc.go +++ b/gnovm/cmd/gno/doc.go @@ -9,6 +9,7 @@ import ( "path/filepath" "github.com/gnolang/gno/gnovm/pkg/doc" + "github.com/gnolang/gno/gnovm/pkg/gnomod" "github.com/gnolang/gno/tm2/pkg/commands" ) @@ -83,8 +84,8 @@ func execDoc(cfg *docCfg, args []string, io *commands.IO) error { return fmt.Errorf("could not determine working directory: %w", err) } - rd, err := findRootDir(wd) - if err != nil && !errors.Is(err, errGnoModNotFound) { + rd, err := gnomod.FindRootDir(wd) + if err != nil && !errors.Is(err, gnomod.ErrGnoModNotFound) { return fmt.Errorf("error determining root gno.mod file: %w", err) } var modDirs []string @@ -111,28 +112,3 @@ func execDoc(cfg *docCfg, args []string, io *commands.IO) error { }, ) } - -var errGnoModNotFound = errors.New("gno.mod file not found in current or any parent directory") - -// XXX: implemented by harry in #799, which at time of writing isn't merged yet. -func findRootDir(absPath string) (string, error) { - if !filepath.IsAbs(absPath) { - return "", errors.New("requires absolute path") - } - - root := filepath.VolumeName(absPath) + string(filepath.Separator) - for absPath != root { - modPath := filepath.Join(absPath, "gno.mod") - _, err := os.Stat(modPath) - if errors.Is(err, os.ErrNotExist) { - absPath = filepath.Dir(absPath) - continue - } - if err != nil { - return "", err - } - return absPath, nil - } - - return "", errGnoModNotFound -} diff --git a/gnovm/pkg/gnomod/utils.go b/gnovm/pkg/gnomod/utils.go index a0c02de8d1f..fe1d0ed79be 100644 --- a/gnovm/pkg/gnomod/utils.go +++ b/gnovm/pkg/gnomod/utils.go @@ -6,6 +6,13 @@ import ( "path/filepath" ) +// ErrGnoModNotFound is returned by [FindRootDir] when, even after traversing +// up to the root directory, a gno.mod file could not be found. +var ErrGnoModNotFound = errors.New("gno.mod file not found in current or any parent directory") + +// FindRootDir determines the root directory of the project which contains the +// gno.mod file. If no gno.mod file is found, [ErrGnoModNotFound] is returned. +// The given path must be absolute. func FindRootDir(absPath string) (string, error) { if !filepath.IsAbs(absPath) { return "", errors.New("requires absolute path") @@ -25,5 +32,5 @@ func FindRootDir(absPath string) (string, error) { return absPath, nil } - return "", errors.New("gno.mod file not found in current or any parent directory") + return "", ErrGnoModNotFound } From 735bd736fbc98d42d068630ac7bee9daac7434de Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Mon, 5 Jun 2023 22:26:41 +0200 Subject: [PATCH 07/11] fix: tests for wrong arguments passed to newDirs --- gnovm/pkg/doc/dirs.go | 26 +++++++++--------- gnovm/pkg/doc/dirs_test.go | 54 +++++++++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/gnovm/pkg/doc/dirs.go b/gnovm/pkg/doc/dirs.go index fff86907248..7e2aee3d258 100644 --- a/gnovm/pkg/doc/dirs.go +++ b/gnovm/pkg/doc/dirs.go @@ -5,6 +5,7 @@ package doc import ( + "fmt" "log" "os" "path" @@ -50,12 +51,9 @@ func newDirs(dirs []string, modDirs []string) *bfsDirs { } for _, mdir := range modDirs { - gm, exists := tryParseGnoMod(filepath.Join(mdir, "gno.mod")) - if gm == nil && exists { - // gno.mod could not be parsed but exists. - // as this would lead us to not having correct import paths (as we can't - // parse the module name from gno.mod), skip this directory; the user has - // received a console warning regardless from tryParseGnoMod. + gm, err := tryParseGnoMod(filepath.Join(mdir, "gno.mod")) + if err != nil { + log.Printf("%v", err) continue } roots = append(roots, bfsDir{ @@ -71,29 +69,29 @@ func newDirs(dirs []string, modDirs []string) *bfsDirs { // tries to parse gno mod file. // second return parameter is whether gno.mod exists. -func tryParseGnoMod(fname string) (*gnomod.File, bool) { +func tryParseGnoMod(fname string) (*gnomod.File, error) { file, err := os.Stat(fname) // early exit for errors and non-file fname if err != nil || file.IsDir() { - if err != nil && !os.IsNotExist(err) { - log.Printf("could not read go.mod file at %q: %v", fname, err) + if err == nil { + return nil, fmt.Errorf("invalid gno.mod at %q: is a directory", fname) } - return nil, !os.IsNotExist(err) + // no need for filename, os errors contain that already + return nil, fmt.Errorf("could not read go.mod file: %w", err) } b, err := os.ReadFile(fname) if err != nil { - log.Printf("could not read go.mod file at %q: %v", fname, err) - return nil, true + return nil, fmt.Errorf("could not read go.mod file: %w", err) } gm, err := gnomod.Parse(fname, b) if err == nil { err = gm.Validate() } if err != nil { - log.Printf("could not parse go.mod file at %q: %v", fname, err) + return nil, fmt.Errorf("error parsing/validating go.mod file at %q: %w", fname, err) } - return gm, true + return gm, nil } func getGnoModDirs(gm *gnomod.File) []bfsDir { diff --git a/gnovm/pkg/doc/dirs_test.go b/gnovm/pkg/doc/dirs_test.go index bbf8bcad70d..9db7ce5357f 100644 --- a/gnovm/pkg/doc/dirs_test.go +++ b/gnovm/pkg/doc/dirs_test.go @@ -1,6 +1,8 @@ package doc import ( + "bytes" + "log" "os" "path/filepath" "strings" @@ -10,16 +12,55 @@ import ( "github.com/stretchr/testify/require" ) -func tNewDirs(t *testing.T) (string, *bfsDirs) { - t.Helper() - +func getwd(t *testing.T) string { wd, err := os.Getwd() require.NoError(t, err) + return wd +} + +func wdJoin(t *testing.T, arg string) string { + return filepath.Join(getwd(t), arg) +} + +func TestNewDirs_nonExisting(t *testing.T) { + old := log.Default().Writer() + var buf bytes.Buffer + log.Default().SetOutput(&buf) + defer func() { log.Default().SetOutput(old) }() // in case of panic + + d := newDirs([]string{wdJoin(t, "non/existing/dir"), wdJoin(t, "testdata/dirsempty")}, []string{wdJoin(t, "and/this/one/neither")}) + for _, ok := d.Next(); ok; _, ok = d.Next() { //nolint:revive + } + log.Default().SetOutput(old) + assert.Empty(t, d.hist, "hist should be empty") + assert.Equal(t, strings.Count(buf.String(), "\n"), 2, "output should contain 2 lines") + assert.Contains(t, buf.String(), "non/existing/dir: no such file or directory") + assert.Contains(t, buf.String(), "this/one/neither/gno.mod: no such file or directory") + assert.NotContains(t, buf.String(), "dirsempty: no such file or directory") +} + +func TestNewDirs_invalidModDir(t *testing.T) { + old := log.Default().Writer() + var buf bytes.Buffer + log.Default().SetOutput(&buf) + defer func() { log.Default().SetOutput(old) }() // in case of panic + + d := newDirs(nil, []string{wdJoin(t, "testdata/dirs")}) + for _, ok := d.Next(); ok; _, ok = d.Next() { //nolint:revive + } + log.Default().SetOutput(old) + assert.Empty(t, d.hist, "hist should be len 0 (testdata/dirs is not a valid mod dir)") + assert.Equal(t, strings.Count(buf.String(), "\n"), 1, "output should contain 1 line") + assert.Contains(t, buf.String(), "gno.mod: no such file or directory") +} + +func tNewDirs(t *testing.T) (string, *bfsDirs) { + t.Helper() // modify GNO_HOME to testdata/dirsdep -- this allows us to test // dependency lookup by dirs. old, ex := os.LookupEnv("GNO_HOME") - os.Setenv("GNO_HOME", filepath.Join(wd, "testdata/dirsdep")) + os.Setenv("GNO_HOME", wdJoin(t, "testdata/dirsdep")) t.Cleanup(func() { if ex { os.Setenv("GNO_HOME", old) @@ -28,8 +69,8 @@ func tNewDirs(t *testing.T) (string, *bfsDirs) { } }) - return filepath.Join(wd, "testdata"), - newDirs([]string{filepath.Join(wd, "testdata/dirs")}, []string{filepath.Join(wd, "testdata/dirsmod")}) + return wdJoin(t, "testdata"), + newDirs([]string{wdJoin(t, "testdata/dirs")}, []string{wdJoin(t, "testdata/dirsmod")}) } func TestDirs_findPackage(t *testing.T) { @@ -87,6 +128,7 @@ func TestDirs_findDir(t *testing.T) { {"crypto/testdata/rand", filepath.Join(td, "dirs/crypto/testdata/rand"), nil}, {"xx", filepath.Join(td, "dirs/xx"), nil}, {"xx2", "/xx2", nil}, + {"2xx", "/2xx", nil}, } for _, tc := range tt { tc := tc From 821c89e2997c0d0efae3c784e780722390387f23 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Mon, 5 Jun 2023 22:28:37 +0200 Subject: [PATCH 08/11] xx --- gnovm/pkg/doc/dirs_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gnovm/pkg/doc/dirs_test.go b/gnovm/pkg/doc/dirs_test.go index 9db7ce5357f..741062b06d4 100644 --- a/gnovm/pkg/doc/dirs_test.go +++ b/gnovm/pkg/doc/dirs_test.go @@ -13,12 +13,14 @@ import ( ) func getwd(t *testing.T) string { + t.Helper() wd, err := os.Getwd() require.NoError(t, err) return wd } func wdJoin(t *testing.T, arg string) string { + t.Helper() return filepath.Join(getwd(t), arg) } From ed6dfadf248d43bc9e436bd178ee82e0fc806e8f Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Mon, 5 Jun 2023 22:38:41 +0200 Subject: [PATCH 09/11] fix dirsempty --- gnovm/pkg/doc/dirs_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gnovm/pkg/doc/dirs_test.go b/gnovm/pkg/doc/dirs_test.go index 741062b06d4..433b707c3a1 100644 --- a/gnovm/pkg/doc/dirs_test.go +++ b/gnovm/pkg/doc/dirs_test.go @@ -30,7 +30,11 @@ func TestNewDirs_nonExisting(t *testing.T) { log.Default().SetOutput(&buf) defer func() { log.Default().SetOutput(old) }() // in case of panic - d := newDirs([]string{wdJoin(t, "non/existing/dir"), wdJoin(t, "testdata/dirsempty")}, []string{wdJoin(t, "and/this/one/neither")}) + // git doesn't track empty directories; so need to create this one on our own. + de := wdJoin(t, "testdata/dirsempty") + require.NoError(t, os.MkdirAll(de, 0o755)) + + d := newDirs([]string{wdJoin(t, "non/existing/dir"), de}, []string{wdJoin(t, "and/this/one/neither")}) for _, ok := d.Next(); ok; _, ok = d.Next() { //nolint:revive } log.Default().SetOutput(old) From 69df3a2cf09ce737b0a98c47648855a49f89d618 Mon Sep 17 00:00:00 2001 From: Morgan Date: Mon, 19 Jun 2023 16:07:45 +0200 Subject: [PATCH 10/11] Apply suggestions from code review Co-authored-by: Hariom Verma --- gnovm/cmd/gno/doc.go | 5 +---- gnovm/pkg/doc/dirs.go | 22 +++++++++---------- .../dirsdep/pkg/mod/dirs.mod/dep/gno.mod | 2 +- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/gnovm/cmd/gno/doc.go b/gnovm/cmd/gno/doc.go index 51b70b57588..c959b872ed7 100644 --- a/gnovm/cmd/gno/doc.go +++ b/gnovm/cmd/gno/doc.go @@ -88,10 +88,7 @@ func execDoc(cfg *docCfg, args []string, io *commands.IO) error { if err != nil && !errors.Is(err, gnomod.ErrGnoModNotFound) { return fmt.Errorf("error determining root gno.mod file: %w", err) } - var modDirs []string - if rd != "" { - modDirs = append(modDirs, rd) - } + modDirs := []string{rd} // select dirs from which to gather directories dirs := []string{filepath.Join(cfg.rootDir, "gnovm/stdlibs"), filepath.Join(cfg.rootDir, "examples")} diff --git a/gnovm/pkg/doc/dirs.go b/gnovm/pkg/doc/dirs.go index 7e2aee3d258..8654a0be425 100644 --- a/gnovm/pkg/doc/dirs.go +++ b/gnovm/pkg/doc/dirs.go @@ -71,25 +71,23 @@ func newDirs(dirs []string, modDirs []string) *bfsDirs { // second return parameter is whether gno.mod exists. func tryParseGnoMod(fname string) (*gnomod.File, error) { file, err := os.Stat(fname) - // early exit for errors and non-file fname - if err != nil || file.IsDir() { - if err == nil { - return nil, fmt.Errorf("invalid gno.mod at %q: is a directory", fname) - } - // no need for filename, os errors contain that already - return nil, fmt.Errorf("could not read go.mod file: %w", err) + if err != nil { + return nil, fmt.Errorf("could not read gno.mod file: %w", err) + } + if file.IsDir() { + return nil, fmt.Errorf("invalid gno.mod at %q: is a directory", fname) } b, err := os.ReadFile(fname) if err != nil { - return nil, fmt.Errorf("could not read go.mod file: %w", err) + return nil, fmt.Errorf("could not read gno.mod file: %w", err) } gm, err := gnomod.Parse(fname, b) - if err == nil { - err = gm.Validate() - } if err != nil { - return nil, fmt.Errorf("error parsing/validating go.mod file at %q: %w", fname, err) + return nil, fmt.Errorf("error parsing gno.mod file at %q: %w", fname, err) + } + if err := gm.Validate(); err != nil { + return nil, fmt.Errorf("error validating gno.mod file at %q: %w", fname, err) } return gm, nil } diff --git a/gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/gno.mod b/gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/gno.mod index 89b5ec87c57..f8dabd6225a 100644 --- a/gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/gno.mod +++ b/gnovm/pkg/doc/testdata/dirsdep/pkg/mod/dirs.mod/dep/gno.mod @@ -1 +1 @@ -package dirs.mod/dep +module dirs.mod/dep From ea60a6c189093c79399cc3b1dbc984f572876cb2 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Mon, 19 Jun 2023 16:08:21 +0200 Subject: [PATCH 11/11] s/tryParse/parse/g --- gnovm/pkg/doc/dirs.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gnovm/pkg/doc/dirs.go b/gnovm/pkg/doc/dirs.go index 8654a0be425..c5a82214ce5 100644 --- a/gnovm/pkg/doc/dirs.go +++ b/gnovm/pkg/doc/dirs.go @@ -51,7 +51,7 @@ func newDirs(dirs []string, modDirs []string) *bfsDirs { } for _, mdir := range modDirs { - gm, err := tryParseGnoMod(filepath.Join(mdir, "gno.mod")) + gm, err := parseGnoMod(filepath.Join(mdir, "gno.mod")) if err != nil { log.Printf("%v", err) continue @@ -67,9 +67,9 @@ func newDirs(dirs []string, modDirs []string) *bfsDirs { return d } -// tries to parse gno mod file. -// second return parameter is whether gno.mod exists. -func tryParseGnoMod(fname string) (*gnomod.File, error) { +// tries to parse gno mod file given the filename, using Parse and Validate from +// the gnomod package +func parseGnoMod(fname string) (*gnomod.File, error) { file, err := os.Stat(fname) if err != nil { return nil, fmt.Errorf("could not read gno.mod file: %w", err)