diff options
| author | matloob <matloob@golang.org> | 2025-12-29 13:16:53 -0500 |
|---|---|---|
| committer | Michael Matloob <matloob@golang.org> | 2026-03-31 11:40:13 -0700 |
| commit | 2000e27ea6a644ea3623db201d8ba2818e8f5838 (patch) | |
| tree | 6777d8bf074776e89adead1cd7c2af8130114756 /src/cmd | |
| parent | f9d2c2fd692e3aa81b80fbe1d0479866abff54df (diff) | |
| download | go-2000e27ea6a644ea3623db201d8ba2818e8f5838.tar.xz | |
cmd/go/internal/doc: use internal go command logic to load packages
We've been using go/build to load packages, and that doesn't
always do the right thing, because it doesn't have the same
context and settings that the go command uses to load packages.
Use the go command's loader to load packages.
This CL doesn't remove the logic for searching for matching
packages in dirs.go. A next step would be to remove that so
all the matching is also done with the go command's logic.
Fixes #75976
Change-Id: I3c76d9a54dc88648bb7c76a17afad8cb6a6a6964
Reviewed-on: https://go-review.googlesource.com/c/go/+/733200
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Sean Liao <sean@liao.dev>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
Reviewed-by: Michael Matloob <matloob@google.com>
Diffstat (limited to 'src/cmd')
| -rw-r--r-- | src/cmd/go/internal/doc/dirs.go | 59 | ||||
| -rw-r--r-- | src/cmd/go/internal/doc/doc.go | 116 | ||||
| -rw-r--r-- | src/cmd/go/internal/doc/doc_test.go | 16 | ||||
| -rw-r--r-- | src/cmd/go/internal/doc/mod.go | 12 | ||||
| -rw-r--r-- | src/cmd/go/internal/doc/pkg.go | 12 | ||||
| -rw-r--r-- | src/cmd/go/internal/doc/pkgsite.go | 8 | ||||
| -rw-r--r-- | src/cmd/go/internal/modload/init.go | 5 | ||||
| -rw-r--r-- | src/cmd/go/testdata/script/doc_http_url.txt | 31 | ||||
| -rw-r--r-- | src/cmd/go/testdata/script/mod_doc.txt | 3 | ||||
| -rw-r--r-- | src/cmd/go/testdata/script/mod_outside.txt | 10 | ||||
| -rw-r--r-- | src/cmd/internal/script/scripttest/readme.go | 1 |
11 files changed, 171 insertions, 102 deletions
diff --git a/src/cmd/go/internal/doc/dirs.go b/src/cmd/go/internal/doc/dirs.go index 5efd40b1d5..86b4b526a3 100644 --- a/src/cmd/go/internal/doc/dirs.go +++ b/src/cmd/go/internal/doc/dirs.go @@ -15,6 +15,9 @@ import ( "strings" "sync" + "cmd/go/internal/cfg" + "cmd/go/internal/modload" + "golang.org/x/mod/semver" ) @@ -41,29 +44,18 @@ var dirs Dirs // dirsInit starts the scanning of package directories in GOROOT and GOPATH. Any // extra paths passed to it are included in the channel. func dirsInit(extra ...Dir) { - if buildCtx.GOROOT == "" { - stdout, err := exec.Command("go", "env", "GOROOT").Output() - if err != nil { - if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { - log.Fatalf("failed to determine GOROOT: $GOROOT is not set and 'go env GOROOT' failed:\n%s", ee.Stderr) - } - log.Fatalf("failed to determine GOROOT: $GOROOT is not set and could not run 'go env GOROOT':\n\t%s", err) - } - buildCtx.GOROOT = string(bytes.TrimSpace(stdout)) - } - dirs.hist = make([]Dir, 0, 1000) dirs.hist = append(dirs.hist, extra...) dirs.scan = make(chan Dir) go dirs.walk(codeRoots()) } -// goCmd returns the "go" command path corresponding to buildCtx.GOROOT. +// goCmd returns the "go" command path corresponding to cfg.GOROOT. func goCmd() string { - if buildCtx.GOROOT == "" { + if cfg.GOROOT == "" { return "go" } - return filepath.Join(buildCtx.GOROOT, "bin", "go") + return filepath.Join(cfg.GOROOT, "bin", "go") } // Reset puts the scan back at the beginning. @@ -187,30 +179,31 @@ var usingModules bool func findCodeRoots() []Dir { var list []Dir if !testGOPATH { - // Check for use of modules by 'go env GOMOD', - // which reports a go.mod file path if modules are enabled. - stdout, _ := exec.Command(goCmd(), "env", "GOMOD").Output() - gomod := string(bytes.TrimSpace(stdout)) - - usingModules = len(gomod) > 0 - if usingModules && buildCtx.GOROOT != "" { - list = append(list, - Dir{dir: filepath.Join(buildCtx.GOROOT, "src"), inModule: true}, - Dir{importPath: "cmd", dir: filepath.Join(buildCtx.GOROOT, "src", "cmd"), inModule: true}) - } + // TODO: use the same state used to load the package. + // For now it's okay to use a new state because we're just + // using it to determine whether we're in module mode. But + // it would be good to avoid an extra run of modload.Init. + if state := modload.NewState(); state.WillBeEnabled() { + usingModules = state.HasModRoot() + if usingModules && cfg.GOROOT != "" { + list = append(list, + Dir{dir: filepath.Join(cfg.GOROOT, "src"), inModule: true}, + Dir{importPath: "cmd", dir: filepath.Join(cfg.GOROOT, "src", "cmd"), inModule: true}) + } - if gomod == os.DevNull { - // Modules are enabled, but the working directory is outside any module. - // We can still access std, cmd, and packages specified as source files - // on the command line, but there are no module roots. - // Avoid 'go list -m all' below, since it will not work. - return list + if !usingModules { + // Modules are enabled, but the working directory is outside any module. + // We can still access std, cmd, and packages specified as source files + // on the command line, but there are no module roots. + // Avoid 'go list -m all' below, since it will not work. + return list + } } } if !usingModules { - if buildCtx.GOROOT != "" { - list = append(list, Dir{dir: filepath.Join(buildCtx.GOROOT, "src")}) + if cfg.GOROOT != "" { + list = append(list, Dir{dir: filepath.Join(cfg.GOROOT, "src")}) } for _, root := range splitGopath() { list = append(list, Dir{dir: filepath.Join(root, "src")}) diff --git a/src/cmd/go/internal/doc/doc.go b/src/cmd/go/internal/doc/doc.go index 4acee8ed42..3ebd0f5dab 100644 --- a/src/cmd/go/internal/doc/doc.go +++ b/src/cmd/go/internal/doc/doc.go @@ -21,6 +21,10 @@ import ( "strings" "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/load" + "cmd/go/internal/modload" + "cmd/go/internal/search" "cmd/internal/telemetry/counter" ) @@ -362,14 +366,29 @@ func failMessage(paths []string, symbol, method string) error { // and there may be more matches. For example, if the argument // is rand.Float64, we must scan both crypto/rand and math/rand // to find the symbol, and the first call will return crypto/rand, true. -func parseArgs(ctx context.Context, flagSet *flag.FlagSet, args []string) (pkg *build.Package, path, symbol string, more bool) { +func parseArgs(ctx context.Context, flagSet *flag.FlagSet, args []string) (pkg *load.Package, path, symbol string, more bool) { wd, err := os.Getwd() if err != nil { log.Fatal(err) } + loader := modload.NewState() + if testGOPATH { + loader = modload.DisabledState() + } + if len(args) > 0 && strings.Index(args[0], "@") >= 0 { + // Version query: force no root + loader.ForceUseModules = true + loader.RootMode = modload.NoRoot + modload.Init(loader) + } else if loader.WillBeEnabled() { + loader.InitWorkfile() + modload.Init(loader) + modload.LoadModFile(loader, context.TODO()) + } + if len(args) == 0 { // Easy: current directory. - return importDir(wd), "", "", false + return mustLoadPackage(ctx, loader, wd), "", "", false } arg := args[0] @@ -388,11 +407,11 @@ func parseArgs(ctx context.Context, flagSet *flag.FlagSet, args []string) (pkg * log.Fatal("cannot use @version with local or absolute paths") } - importPkg := func(p string) (*build.Package, error) { + importPkg := func(p string) (*load.Package, error) { if version != "" { - return loadVersioned(ctx, p, version) + return loadVersioned(ctx, loader, p, version) } - return build.Import(p, wd, build.ImportComment) + return loadPackage(ctx, loader, p) } switch len(args) { @@ -407,16 +426,16 @@ func parseArgs(ctx context.Context, flagSet *flag.FlagSet, args []string) (pkg * return pkg, arg, args[1], false } for { - dir, importPath, ok := findNextPackage(arg) + importPath, ok := findNextPackage(arg) if !ok { break } if version != "" { - if pkg, err = loadVersioned(ctx, importPath, version); err == nil { + if pkg, err = loadVersioned(ctx, loader, importPath, version); err == nil { return pkg, arg, args[1], true } } else { - if pkg, err = build.ImportDir(dir, build.ImportComment); err == nil { + if pkg, err = loadPackage(ctx, loader, importPath); err == nil { return pkg, arg, args[1], true } } @@ -434,7 +453,7 @@ func parseArgs(ctx context.Context, flagSet *flag.FlagSet, args []string) (pkg * // package paths as their prefix. var importErr error if filepath.IsAbs(arg) { - pkg, importErr = build.ImportDir(arg, build.ImportComment) + pkg, importErr = loadPackage(ctx, loader, arg) if importErr == nil { return pkg, arg, "", false } @@ -449,7 +468,7 @@ func parseArgs(ctx context.Context, flagSet *flag.FlagSet, args []string) (pkg * // Kills the problem caused by case-insensitive file systems // matching an upper case name as a package name. if !strings.ContainsAny(arg, `/\`) && token.IsExported(arg) { - pkg, err := build.ImportDir(".", build.ImportComment) + pkg, err := loadPackage(ctx, loader, ".") if err == nil { return pkg, "", arg, false } @@ -475,7 +494,7 @@ func parseArgs(ctx context.Context, flagSet *flag.FlagSet, args []string) (pkg * symbol = arg[period+1:] } // Have we identified a package already? - pkg, err := importPkg(arg[0:period]) + pkg, err := loadPackage(ctx, loader, arg[0:period]) if err == nil { return pkg, arg[0:period], symbol, false } @@ -483,18 +502,16 @@ func parseArgs(ctx context.Context, flagSet *flag.FlagSet, args []string) (pkg * // or ivy/value for robpike.io/ivy/value. pkgName := arg[:period] for { - dir, importPath, ok := findNextPackage(pkgName) + importPath, ok := findNextPackage(pkgName) if !ok { break } if version != "" { - if pkg, err = loadVersioned(ctx, importPath, version); err == nil { - return pkg, arg[0:period], symbol, true - } - } else { - if pkg, err = build.ImportDir(dir, build.ImportComment); err == nil { + if pkg, err = loadVersioned(ctx, loader, importPath, version); err == nil { return pkg, arg[0:period], symbol, true } + } else if pkg, err = loadPackage(ctx, loader, importPath); err == nil { + return pkg, arg[0:period], symbol, true } } dirs.Reset() // Next iteration of for loop must scan all the directories again. @@ -506,7 +523,7 @@ func parseArgs(ctx context.Context, flagSet *flag.FlagSet, args []string) (pkg * if version == "" { version = v } - pkg, err := loadVersioned(ctx, pkgPath, version) + pkg, err := loadVersioned(ctx, loader, pkgPath, version) if err == nil { return pkg, pkgPath, "", false } @@ -536,7 +553,38 @@ func parseArgs(ctx context.Context, flagSet *flag.FlagSet, args []string) (pkg * } } // Guess it's a symbol in the current directory. - return importDir(wd), "", arg, false + return mustLoadPackage(ctx, loader, wd), "", arg, false +} + +func loadPackage(ctx context.Context, loader *modload.State, pattern string) (*load.Package, error) { + if !search.NewMatch(pattern).IsLiteral() { + return nil, fmt.Errorf("pattern %q does not specify a single package", pattern) + } + + pkgOpts := load.PackageOpts{ + IgnoreImports: true, + SuppressBuildInfo: true, + SuppressEmbedFiles: true, + } + pkgs := load.PackagesAndErrors(loader, ctx, pkgOpts, []string{pattern}) + + if len(pkgs) != 1 { + return nil, fmt.Errorf("path %q matched multiple packages", pattern) + } + + p := pkgs[0] + if p.Error != nil { + return nil, p.Error + } + return p, nil +} + +func mustLoadPackage(ctx context.Context, loader *modload.State, dir string) *load.Package { + pkg, err := loadPackage(ctx, loader, dir) + if err != nil { + log.Fatal(err) + } + return pkg } // dotPaths lists all the dotted paths legal on Unix-like and @@ -564,15 +612,6 @@ func isDotSlash(arg string) bool { return false } -// importDir is just an error-catching wrapper for build.ImportDir. -func importDir(dir string) *build.Package { - pkg, err := build.ImportDir(dir, build.ImportComment) - if err != nil { - log.Fatal(err) - } - return pkg -} - // parseSymbol breaks str apart into a symbol and method. // Both may be missing or the method may be missing. // If present, each must be a valid Go identifier. @@ -600,36 +639,33 @@ func isExported(name string) bool { return unexported || token.IsExported(name) } -// findNextPackage returns the next full file name path and import path that -// matches the (perhaps partial) package path pkg. The boolean reports if -// any match was found. -func findNextPackage(pkg string) (string, string, bool) { +// findNextPackage returns the next import path that matches the +// (perhaps partial) package path pkg. The boolean reports if any match was found. +func findNextPackage(pkg string) (string, bool) { if filepath.IsAbs(pkg) { if dirs.offset == 0 { dirs.offset = -1 - return pkg, "", true + return pkg, true } - return "", "", false + return "", false } if pkg == "" || token.IsExported(pkg) { // Upper case symbol cannot be a package name. - return "", "", false + return "", false } pkg = path.Clean(pkg) pkgSuffix := "/" + pkg for { d, ok := dirs.Next() if !ok { - return "", "", false + return "", false } if d.importPath == pkg || strings.HasSuffix(d.importPath, pkgSuffix) { - return d.dir, d.importPath, true + return d.importPath, true } } } -var buildCtx = build.Default - // splitGopath splits $GOPATH into a list of roots. func splitGopath() []string { - return filepath.SplitList(buildCtx.GOPATH) + return filepath.SplitList(cfg.BuildContext.GOPATH) } diff --git a/src/cmd/go/internal/doc/doc_test.go b/src/cmd/go/internal/doc/doc_test.go index 3eeaffc25b..cd931e33c0 100644 --- a/src/cmd/go/internal/doc/doc_test.go +++ b/src/cmd/go/internal/doc/doc_test.go @@ -7,7 +7,6 @@ package doc import ( "bytes" "flag" - "go/build" "internal/testenv" "log" "os" @@ -16,18 +15,19 @@ import ( "runtime" "strings" "testing" + + "cmd/go/internal/cfg" ) func TestMain(m *testing.M) { // Clear GOPATH so we don't access the user's own packages in the test. - buildCtx.GOPATH = "" + cfg.BuildContext.GOPATH = "" testGOPATH = true // force GOPATH mode; module test is in cmd/go/testdata/script/mod_doc.txt // Set GOROOT in case runtime.GOROOT is wrong (for example, if the test was // built with -trimpath). dirsInit would identify it using 'go env GOROOT', // but we can't be sure that the 'go' in $PATH is the right one either. - buildCtx.GOROOT = testenv.GOROOT(nil) - build.Default.GOROOT = testenv.GOROOT(nil) + cfg.GOROOT = testenv.GOROOT(nil) // Add $GOROOT/src/cmd/go/internal/doc/testdata explicitly so we can access its contents in the test. // Normally testdata directories are ignored, but sending it to dirs.scan directly is @@ -37,9 +37,9 @@ func TestMain(m *testing.M) { panic(err) } dirsInit( - Dir{importPath: "testdata", dir: testdataDir}, - Dir{importPath: "testdata/nested", dir: filepath.Join(testdataDir, "nested")}, - Dir{importPath: "testdata/nested/nested", dir: filepath.Join(testdataDir, "nested", "nested")}) + Dir{importPath: "cmd/go/internal/doc/testdata", dir: testdataDir}, + Dir{importPath: "cmd/go/internal/doc/testdata/nested", dir: filepath.Join(testdataDir, "nested")}, + Dir{importPath: "cmd/go/internal/doc/testdata/nested/nested", dir: filepath.Join(testdataDir, "nested", "nested")}) os.Exit(m.Run()) } @@ -1204,7 +1204,7 @@ func TestDotSlashLookup(t *testing.T) { t.Skip("scanning file system takes too long") } maybeSkip(t) - t.Chdir(filepath.Join(buildCtx.GOROOT, "src", "text")) + t.Chdir(filepath.Join(cfg.GOROOT, "src", "text")) var b strings.Builder var flagSet flag.FlagSet diff --git a/src/cmd/go/internal/doc/mod.go b/src/cmd/go/internal/doc/mod.go index a15ff29526..e139cc1208 100644 --- a/src/cmd/go/internal/doc/mod.go +++ b/src/cmd/go/internal/doc/mod.go @@ -8,7 +8,6 @@ import ( "context" "debug/buildinfo" "fmt" - "go/build" "os/exec" "cmd/go/internal/load" @@ -16,24 +15,19 @@ import ( ) // loadVersioned loads a package at a specific version. -func loadVersioned(ctx context.Context, pkgPath, version string) (*build.Package, error) { - loaderState := modload.NewState() - loaderState.ForceUseModules = true - loaderState.RootMode = modload.NoRoot - modload.Init(loaderState) - +func loadVersioned(ctx context.Context, loader *modload.State, pkgPath, version string) (*load.Package, error) { var opts load.PackageOpts args := []string{ fmt.Sprintf("%s@%s", pkgPath, version), } - pkgs, err := load.PackagesAndErrorsOutsideModule(loaderState, ctx, opts, args) + pkgs, err := load.PackagesAndErrorsOutsideModule(loader, ctx, opts, args) if err != nil { return nil, err } if len(pkgs) != 1 { return nil, fmt.Errorf("incorrect number of packages: want 1, got %d", len(pkgs)) } - return pkgs[0].Internal.Build, nil + return pkgs[0], nil } // inferVersion checks if the argument matches a command on $PATH and returns its module path and version. diff --git a/src/cmd/go/internal/doc/pkg.go b/src/cmd/go/internal/doc/pkg.go index c531595f86..bd4a2f6e4f 100644 --- a/src/cmd/go/internal/doc/pkg.go +++ b/src/cmd/go/internal/doc/pkg.go @@ -9,7 +9,6 @@ import ( "bytes" "fmt" "go/ast" - "go/build" "go/doc" "go/format" "go/parser" @@ -22,6 +21,9 @@ import ( "strings" "unicode" "unicode/utf8" + + "cmd/go/internal/cfg" + "cmd/go/internal/load" ) const ( @@ -36,7 +38,7 @@ type Package struct { pkg *ast.Package // Parsed package. file *ast.File // Merged from all files in the package doc *doc.Package - build *build.Package + build *load.Package typedValue map[*doc.Value]bool // Consts and vars related to types. constructor map[*doc.Func]bool // Constructors. fs *token.FileSet // Needed for printing. @@ -96,8 +98,8 @@ func (pkg *Package) prettyPath() string { // Also convert everything to slash-separated paths for uniform handling. path = filepath.Clean(filepath.ToSlash(pkg.build.Dir)) // Can we find a decent prefix? - if buildCtx.GOROOT != "" { - goroot := filepath.Join(buildCtx.GOROOT, "src") + if cfg.GOROOT != "" { + goroot := filepath.Join(cfg.GOROOT, "src") if p, ok := trim(path, filepath.ToSlash(goroot)); ok { return p } @@ -137,7 +139,7 @@ func (pkg *Package) Fatalf(format string, args ...any) { // parsePackage turns the build package we found into a parsed package // we can then use to generate documentation. -func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Package { +func parsePackage(writer io.Writer, pkg *load.Package, userPath string) *Package { // include tells parser.ParseDir which files to include. // That means the file must be in the build package's GoFiles, CgoFiles, // TestGoFiles or XTestGoFiles list only (no tag-ignored files, swig or diff --git a/src/cmd/go/internal/doc/pkgsite.go b/src/cmd/go/internal/doc/pkgsite.go index dc344cbbca..2c135cdc34 100644 --- a/src/cmd/go/internal/doc/pkgsite.go +++ b/src/cmd/go/internal/doc/pkgsite.go @@ -16,6 +16,8 @@ import ( "os/signal" "path/filepath" "strings" + + "cmd/go/internal/cfg" ) // pickUnusedPort finds an unused port by trying to listen on port 0 @@ -48,6 +50,10 @@ func doPkgsite(urlPath, fragment string) error { path += "#" + fragment } + if file := os.Getenv("TEST_GODOC_URL_FILE"); file != "" { + return os.WriteFile(file, []byte(path+"\n"), 0666) + } + // Turn off the default signal handler for SIGINT (and SIGQUIT on Unix) // and instead wait for the child process to handle the signal and // exit before exiting ourselves. @@ -73,7 +79,7 @@ func doPkgsite(urlPath, fragment string) error { const version = "v0.0.0-20251223195805-1a3bd3c788fe" cmd := exec.Command(goCmd(), "run", "golang.org/x/pkgsite/cmd/internal/doc@"+version, - "-gorepo", buildCtx.GOROOT, + "-gorepo", cfg.GOROOT, "-http", addr, "-open", path) cmd.Env = env diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index 8501d3f5ee..e71e467d9f 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -467,6 +467,11 @@ func NewState() *State { return s } +func DisabledState() *State { + fips140.Init() + return &State{initialized: true, modulesEnabled: false} +} + func (s *State) Fetcher() *modfetch.Fetcher { return s.fetcher } diff --git a/src/cmd/go/testdata/script/doc_http_url.txt b/src/cmd/go/testdata/script/doc_http_url.txt new file mode 100644 index 0000000000..19947dd770 --- /dev/null +++ b/src/cmd/go/testdata/script/doc_http_url.txt @@ -0,0 +1,31 @@ +env TEST_GODOC_URL_FILE=$WORK/url.txt + +# Outside of a module. +go doc -http +grep '/std' $TEST_GODOC_URL_FILE + +# Inside a module with a major version suffix. +# We should use the major version suffix rather than +# the location in the gopath (#75976). +cd example.com/m +go doc -http +grep '/example.com/m/v5' $TEST_GODOC_URL_FILE +go doc -http . +grep '/example.com/m/v5' $TEST_GODOC_URL_FILE + +# In GOPATH mode, we should use the location in the GOPATH. +env GO111MODULE=off +go doc -http +grep '/std' $TEST_GODOC_URL_FILE +# TODO(matloob): This should probably this be the same as 'go doc -http .' +! grep '/example.com/m' $TEST_GODOC_URL_FILE +go doc -http . +grep '/example.com/m' $TEST_GODOC_URL_FILE +! grep '/example.com/m/v5' $TEST_GODOC_URL_FILE + +-- example.com/m/go.mod -- +module example.com/m/v5 + +go 1.27 +-- example.com/m/m.go -- +package m
\ No newline at end of file diff --git a/src/cmd/go/testdata/script/mod_doc.txt b/src/cmd/go/testdata/script/mod_doc.txt index bf0a19d770..cbfd9336dd 100644 --- a/src/cmd/go/testdata/script/mod_doc.txt +++ b/src/cmd/go/testdata/script/mod_doc.txt @@ -38,9 +38,8 @@ go doc rsc.io/quote stdout 'Package quote collects pithy sayings.' # Check that a sensible error message is printed when a package is not found. -env GOPROXY=off ! go doc example.com/hello -stderr '^doc: cannot find module providing package example.com/hello: module lookup disabled by GOPROXY=off$' +stderr 'doc: no required module provides package example.com/hello; to add it:\n\s+go get example.com/hello' # When in a module with a vendor directory, doc should use the vendored copies # of the packages. 'std' and 'cmd' are convenient examples of such modules. diff --git a/src/cmd/go/testdata/script/mod_outside.txt b/src/cmd/go/testdata/script/mod_outside.txt index 7a0dc9f22f..18cc3d5a27 100644 --- a/src/cmd/go/testdata/script/mod_outside.txt +++ b/src/cmd/go/testdata/script/mod_outside.txt @@ -173,13 +173,15 @@ go build -n fmt go build ./newgo/newgo.go # 'go doc' without arguments implicitly operates on the current directory, and should fail. -# TODO(golang.org/issue/32027): currently, it succeeds. cd needmod -go doc +! go doc +stderr '^doc: go: go.mod file not found in current directory or any parent directory; see ''go help modules''$' cd .. -# 'go doc' of a non-module directory should also succeed. -go doc ./needmod +# 'go doc' of a non-module directory should also fail. +! go doc ./needmod +stderr '^doc: go: go.mod file not found in current directory or any parent directory; see ''go help modules''$' + # 'go doc' should succeed for standard-library packages. go doc fmt diff --git a/src/cmd/internal/script/scripttest/readme.go b/src/cmd/internal/script/scripttest/readme.go index af7397223f..8f333b06c2 100644 --- a/src/cmd/internal/script/scripttest/readme.go +++ b/src/cmd/internal/script/scripttest/readme.go @@ -37,6 +37,7 @@ func checkScriptReadme(t *testing.T, engine *script.Engine, env []string, script doc := new(strings.Builder) cmd := testenv.Command(t, gotool, "doc", "cmd/internal/script") + cmd.Dir = t.TempDir() // make sure the test is not running inside the std or cmd module of another GOROOT cmd.Env = env cmd.Stdout = doc if err := cmd.Run(); err != nil { |
