// to -f '{{.ImportPath}}'. The struct being passed to the template is:
//
// type Package struct {
-// Dir string // directory containing package sources
-// ImportPath string // import path of package in dir
-// ImportComment string // path in import comment on package statement
-// Name string // package name
-// Doc string // package documentation string
-// Target string // install path
-// Shlib string // the shared library that contains this package (only set when -linkshared)
-// Goroot bool // is this package in the Go root?
-// Standard bool // is this package part of the standard Go library?
-// Stale bool // would 'go install' do anything for this package?
-// StaleReason string // explanation for Stale==true
-// Root string // Go root or Go path dir containing this package
-// ConflictDir string // this directory shadows Dir in $GOPATH
-// BinaryOnly bool // binary-only package (no longer supported)
-// ForTest string // package is only for use in named test
-// Export string // file containing export data (when using -export)
-// BuildID string // build ID of the compiled package (when using -export)
-// Module *Module // info about package's containing module, if any (can be nil)
-// Match []string // command-line patterns matching this package
-// DepOnly bool // package is only a dependency, not explicitly listed
+// Dir string // directory containing package sources
+// ImportPath string // import path of package in dir
+// ImportComment string // path in import comment on package statement
+// Name string // package name
+// Doc string // package documentation string
+// Target string // install path
+// Shlib string // the shared library that contains this package (only set when -linkshared)
+// Goroot bool // is this package in the Go root?
+// Standard bool // is this package part of the standard Go library?
+// Stale bool // would 'go install' do anything for this package?
+// StaleReason string // explanation for Stale==true
+// Root string // Go root or Go path dir containing this package
+// ConflictDir string // this directory shadows Dir in $GOPATH
+// BinaryOnly bool // binary-only package (no longer supported)
+// ForTest string // package is only for use in named test
+// Export string // file containing export data (when using -export)
+// BuildID string // build ID of the compiled package (when using -export)
+// Module *Module // info about package's containing module, if any (can be nil)
+// Match []string // command-line patterns matching this package
+// DepOnly bool // package is only a dependency, not explicitly listed
+// DefaultGODEBUG string // default GODEBUG setting, for main packages
//
// // Source files
-// GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
-// CgoFiles []string // .go source files that import "C"
-// CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
-// IgnoredGoFiles []string // .go source files ignored due to build constraints
+// GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+// CgoFiles []string // .go source files that import "C"
+// CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
+// IgnoredGoFiles []string // .go source files ignored due to build constraints
// IgnoredOtherFiles []string // non-.go source files ignored due to build constraints
-// CFiles []string // .c source files
-// CXXFiles []string // .cc, .cxx and .cpp source files
-// MFiles []string // .m source files
-// HFiles []string // .h, .hh, .hpp and .hxx source files
-// FFiles []string // .f, .F, .for and .f90 Fortran source files
-// SFiles []string // .s source files
-// SwigFiles []string // .swig files
-// SwigCXXFiles []string // .swigcxx files
-// SysoFiles []string // .syso object files to add to archive
-// TestGoFiles []string // _test.go files in package
-// XTestGoFiles []string // _test.go files outside package
+// CFiles []string // .c source files
+// CXXFiles []string // .cc, .cxx and .cpp source files
+// MFiles []string // .m source files
+// HFiles []string // .h, .hh, .hpp and .hxx source files
+// FFiles []string // .f, .F, .for and .f90 Fortran source files
+// SFiles []string // .s source files
+// SwigFiles []string // .swig files
+// SwigCXXFiles []string // .swigcxx files
+// SysoFiles []string // .syso object files to add to archive
+// TestGoFiles []string // _test.go files in package
+// XTestGoFiles []string // _test.go files outside package
//
// // Embedded files
// EmbedPatterns []string // //go:embed patterns
to -f '{{.ImportPath}}'. The struct being passed to the template is:
type Package struct {
- Dir string // directory containing package sources
- ImportPath string // import path of package in dir
- ImportComment string // path in import comment on package statement
- Name string // package name
- Doc string // package documentation string
- Target string // install path
- Shlib string // the shared library that contains this package (only set when -linkshared)
- Goroot bool // is this package in the Go root?
- Standard bool // is this package part of the standard Go library?
- Stale bool // would 'go install' do anything for this package?
- StaleReason string // explanation for Stale==true
- Root string // Go root or Go path dir containing this package
- ConflictDir string // this directory shadows Dir in $GOPATH
- BinaryOnly bool // binary-only package (no longer supported)
- ForTest string // package is only for use in named test
- Export string // file containing export data (when using -export)
- BuildID string // build ID of the compiled package (when using -export)
- Module *Module // info about package's containing module, if any (can be nil)
- Match []string // command-line patterns matching this package
- DepOnly bool // package is only a dependency, not explicitly listed
+ Dir string // directory containing package sources
+ ImportPath string // import path of package in dir
+ ImportComment string // path in import comment on package statement
+ Name string // package name
+ Doc string // package documentation string
+ Target string // install path
+ Shlib string // the shared library that contains this package (only set when -linkshared)
+ Goroot bool // is this package in the Go root?
+ Standard bool // is this package part of the standard Go library?
+ Stale bool // would 'go install' do anything for this package?
+ StaleReason string // explanation for Stale==true
+ Root string // Go root or Go path dir containing this package
+ ConflictDir string // this directory shadows Dir in $GOPATH
+ BinaryOnly bool // binary-only package (no longer supported)
+ ForTest string // package is only for use in named test
+ Export string // file containing export data (when using -export)
+ BuildID string // build ID of the compiled package (when using -export)
+ Module *Module // info about package's containing module, if any (can be nil)
+ Match []string // command-line patterns matching this package
+ DepOnly bool // package is only a dependency, not explicitly listed
+ DefaultGODEBUG string // default GODEBUG setting, for main packages
// Source files
- GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
- CgoFiles []string // .go source files that import "C"
- CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
- IgnoredGoFiles []string // .go source files ignored due to build constraints
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string // .go source files that import "C"
+ CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
+ IgnoredGoFiles []string // .go source files ignored due to build constraints
IgnoredOtherFiles []string // non-.go source files ignored due to build constraints
- CFiles []string // .c source files
- CXXFiles []string // .cc, .cxx and .cpp source files
- MFiles []string // .m source files
- HFiles []string // .h, .hh, .hpp and .hxx source files
- FFiles []string // .f, .F, .for and .f90 Fortran source files
- SFiles []string // .s source files
- SwigFiles []string // .swig files
- SwigCXXFiles []string // .swigcxx files
- SysoFiles []string // .syso object files to add to archive
- TestGoFiles []string // _test.go files in package
- XTestGoFiles []string // _test.go files outside package
+ CFiles []string // .c source files
+ CXXFiles []string // .cc, .cxx and .cpp source files
+ MFiles []string // .m source files
+ HFiles []string // .h, .hh, .hpp and .hxx source files
+ FFiles []string // .f, .F, .for and .f90 Fortran source files
+ SFiles []string // .s source files
+ SwigFiles []string // .swig files
+ SwigCXXFiles []string // .swigcxx files
+ SysoFiles []string // .syso object files to add to archive
+ TestGoFiles []string // _test.go files in package
+ XTestGoFiles []string // _test.go files outside package
// Embedded files
EmbedPatterns []string // //go:embed patterns
--- /dev/null
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package load
+
+import (
+ "cmd/go/internal/modload"
+ "errors"
+ "fmt"
+ "go/build"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+var ErrNotGoDebug = errors.New("not //go:debug line")
+
+func ParseGoDebug(text string) (key, value string, err error) {
+ if !strings.HasPrefix(text, "//go:debug") {
+ return "", "", ErrNotGoDebug
+ }
+ i := strings.IndexAny(text, " \t")
+ if i < 0 {
+ if strings.TrimSpace(text) == "//go:debug" {
+ return "", "", fmt.Errorf("missing key=value")
+ }
+ return "", "", ErrNotGoDebug
+ }
+ k, v, ok := strings.Cut(strings.TrimSpace(text[i:]), "=")
+ if !ok {
+ return "", "", fmt.Errorf("missing key=value")
+ }
+ if strings.ContainsAny(k, " \t") {
+ return "", "", fmt.Errorf("key contains space")
+ }
+ if strings.ContainsAny(v, " \t") {
+ return "", "", fmt.Errorf("value contains space")
+ }
+ if strings.ContainsAny(k, ",") {
+ return "", "", fmt.Errorf("key contains comma")
+ }
+ if strings.ContainsAny(v, ",") {
+ return "", "", fmt.Errorf("value contains comma")
+ }
+ return k, v, nil
+}
+
+// defaultGODEBUG returns the default GODEBUG setting for the main package p.
+// When building a test binary, directives, testDirectives, and xtestDirectives
+// list additional directives from the package under test.
+func defaultGODEBUG(p *Package, directives, testDirectives, xtestDirectives []build.Directive) string {
+ if p.Name != "main" {
+ return ""
+ }
+ goVersion := modload.MainModules.GoVersion()
+ if modload.RootMode == modload.NoRoot && p.Module != nil {
+ // This is go install pkg@version or go run pkg@version.
+ // Use the Go version from the package.
+ // If there isn't one, then
+ goVersion = p.Module.GoVersion
+ if goVersion == "" {
+ goVersion = "1.20"
+ }
+ }
+
+ m := godebugForGoVersion(goVersion)
+ for _, list := range [][]build.Directive{p.Internal.Build.Directives, directives, testDirectives, xtestDirectives} {
+ for _, d := range list {
+ k, v, err := ParseGoDebug(d.Text)
+ if err != nil {
+ continue
+ }
+ if m == nil {
+ m = make(map[string]string)
+ }
+ m[k] = v
+ }
+ }
+ var keys []string
+ for k := range m {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ var b strings.Builder
+ for _, k := range keys {
+ if b.Len() > 0 {
+ b.WriteString(",")
+ }
+ b.WriteString(k)
+ b.WriteString("=")
+ b.WriteString(m[k])
+ }
+ return b.String()
+}
+
+func godebugForGoVersion(v string) map[string]string {
+ if strings.Count(v, ".") >= 2 {
+ i := strings.Index(v, ".")
+ j := i + 1 + strings.Index(v[i+1:], ".")
+ v = v[:j]
+ }
+
+ if !strings.HasPrefix(v, "1.") {
+ return nil
+ }
+ n, err := strconv.Atoi(v[len("1."):])
+ if err != nil {
+ return nil
+ }
+
+ def := make(map[string]string)
+ for _, d := range defaultGodebugs {
+ if (d.before != 0 && n < d.before) || (d.first != 0 && n >= d.first) {
+ def[d.name] = d.value
+ }
+ }
+ return def
+}
+
+var defaultGodebugs = []struct {
+ before int // applies to Go versions up until this one (21 for Go 1.21)
+ first int // applies to Go versions starting at this one (21 for Go 1.21)
+ name string
+ value string
+}{
+ {before: 21, name: "panicnil", value: "1"},
+}
type PackagePublic struct {
// Note: These fields are part of the go command's public API.
// See list.go. It is okay to add fields, but not to change or
- // remove existing ones. Keep in sync with list.go
+ // remove existing ones. Keep in sync with ../list/list.go
Dir string `json:",omitempty"` // directory containing package sources
ImportPath string `json:",omitempty"` // import path of package in dir
ImportComment string `json:",omitempty"` // path in import comment on package statement
BinaryOnly bool `json:",omitempty"` // package cannot be recompiled
Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies?
+ DefaultGODEBUG string `json:",omitempty"` // default GODEBUG setting (only for Name=="main")
+
// Stale and StaleReason remain here *only* for the list command.
// They are only initialized in preparation for list execution.
// The regular build determines staleness on the fly during action execution.
TestmainGo *[]byte // content for _testmain.go
Embed map[string][]string // //go:embed comment mapping
OrigImportPath string // original import path before adding '_test' suffix
- Directives []build.Directive
- TestDirectives []build.Directive
- XTestDirectives []build.Directive
Asmflags []string // -asmflags for this package
Gcflags []string // -gcflags for this package
p.TestEmbedPatterns = pp.TestEmbedPatterns
p.XTestEmbedPatterns = pp.XTestEmbedPatterns
p.Internal.OrigImportPath = pp.ImportPath
- p.Internal.Directives = pp.Directives
- p.Internal.TestDirectives = pp.TestDirectives
- p.Internal.XTestDirectives = pp.XTestDirectives
}
// A PackageError describes an error loading information about a package.
if cfg.ModulesEnabled {
p.Module = modload.PackageModuleInfo(ctx, pkgPath)
}
+ p.DefaultGODEBUG = defaultGODEBUG(p, nil, nil, nil)
p.EmbedFiles, p.Internal.Embed, err = resolveEmbed(p.Dir, p.EmbedPatterns)
if err != nil {
if cfg.BuildTrimpath {
appendSetting("-trimpath", "true")
}
+ if p.DefaultGODEBUG != "" {
+ appendSetting("DefaultGODEBUG", p.DefaultGODEBUG)
+ }
cgo := "0"
if cfg.BuildContext.CgoEnabled {
cgo = "1"
"cmd/go/internal/cfg"
"cmd/go/internal/fsys"
+ "cmd/go/internal/slices"
"cmd/go/internal/str"
"cmd/go/internal/trace"
)
ptest.Internal.Embed = testEmbed
ptest.EmbedFiles = str.StringList(p.EmbedFiles, p.TestEmbedFiles)
ptest.Internal.OrigImportPath = p.Internal.OrigImportPath
+ ptest.Internal.Build.Directives = append(slices.Clip(p.Internal.Build.Directives), p.Internal.Build.TestDirectives...)
ptest.collectDeps()
} else {
ptest = p
Internal: PackageInternal{
LocalPrefix: p.Internal.LocalPrefix,
Build: &build.Package{
- ImportPos: p.Internal.Build.XTestImportPos,
+ ImportPos: p.Internal.Build.XTestImportPos,
+ Directives: p.Internal.Build.XTestDirectives,
},
Imports: ximports,
RawImports: rawXTestImports,
},
}
+ pb := p.Internal.Build
+ pmain.DefaultGODEBUG = defaultGODEBUG(pmain, pb.Directives, pb.TestDirectives, pb.XTestDirectives)
+
// The generated main also imports testing, regexp, and os.
// Also the linker introduces implicit dependencies reported by LinkerDeps.
stk.Push("testmain")
// GoVersion returns the go version set on the single module, in module mode,
// or the go.work file in workspace mode.
func (mms *MainModuleSet) GoVersion() string {
- if !inWorkspaceMode() {
+ switch {
+ case inWorkspaceMode():
+ v := mms.workFileGoVersion
+ if v == "" {
+ // Fall back to 1.18 for go.work files.
+ v = "1.18"
+ }
+ return v
+ case mms == nil || len(mms.versions) == 0:
+ return "1.18"
+ default:
return modFileGoVersion(mms.ModFile(mms.mustGetSingleMainModule()))
}
- v := mms.workFileGoVersion
- if v == "" {
- // Fall back to 1.18 for go.work files.
- v = "1.18"
- }
- return v
}
func (mms *MainModuleSet) WorkFileReplaceMap() map[module.Version]module.Version {
data, f, err := ReadModFile(gomod, fixVersion(ctx, &fixed))
if err != nil {
if inWorkspaceMode() {
- base.Fatalf("go: cannot load module listed in go.work file: %v", err)
+ base.Fatalf("go: cannot load module %s listed in go.work file: %v", base.ShortPath(gomod), err)
} else {
base.Fatalf("go: %v", err)
}
"encoding/json"
"errors"
"fmt"
+ "go/token"
"internal/coverage"
"internal/lazyregexp"
"io"
return errors.New("binary-only packages are no longer supported")
}
+ if err := b.checkDirectives(a); err != nil {
+ return err
+ }
+
if err := b.Mkdir(a.Objdir); err != nil {
return err
}
return nil
}
+func (b *Builder) checkDirectives(a *Action) error {
+ var msg *bytes.Buffer
+ p := a.Package
+ var seen map[string]token.Position
+ for _, d := range p.Internal.Build.Directives {
+ if strings.HasPrefix(d.Text, "//go:debug") {
+ key, _, err := load.ParseGoDebug(d.Text)
+ if err != nil && err != load.ErrNotGoDebug {
+ if msg == nil {
+ msg = new(bytes.Buffer)
+ }
+ fmt.Fprintf(msg, "%s: invalid //go:debug: %v\n", d.Pos, err)
+ continue
+ }
+ if pos, ok := seen[key]; ok {
+ fmt.Fprintf(msg, "%s: repeated //go:debug for %v\n\t%s: previous //go:debug\n", d.Pos, key, pos)
+ continue
+ }
+ if seen == nil {
+ seen = make(map[string]token.Position)
+ }
+ seen[key] = d.Pos
+ }
+ }
+ if msg != nil {
+ return formatOutput(b.WorkDir, p.Dir, p.ImportPath, p.Desc(), b.processOutput(msg.Bytes()))
+
+ }
+ return nil
+}
+
func (b *Builder) cacheObjdirFile(a *Action, c *cache.Cache, name string) error {
f, err := os.Open(a.Objdir + name)
if err != nil {
}
}
+ // Store default GODEBUG in binaries.
+ if root.Package.DefaultGODEBUG != "" {
+ ldflags = append(ldflags, "-X=runtime.godebugDefault="+root.Package.DefaultGODEBUG)
+ }
+
// If the user has not specified the -extld option, then specify the
// appropriate linker. In case of C++ code, use the compiler named
// by the CXX environment variable or defaultCXX if CXX is not set.
--- /dev/null
+rsc.io/panicnil v1.0.0
+written by hand
+
+-- .mod --
+module rsc.io/panicnil
+-- .info --
+{"Version":"v1.0.0"}
+-- fortune.go --
+package main
+
+func main() {
+ panic(nil)
+}
--- /dev/null
+rsc.io/panicnil v1.1.0
+written by hand
+
+-- .mod --
+module rsc.io/panicnil
+go 1.21
+-- .info --
+{"Version":"v1.1.0"}
+-- fortune.go --
+package main
+
+func main() {
+ panic(nil)
+}
--- /dev/null
+env GO111MODULE=on
+env GOTRACEBACK=single
+
+# Go 1.21 work module should leave panicnil with an implicit default.
+cp go.mod.21 go.mod
+go list -f '{{.Module.GoVersion}} {{.DefaultGODEBUG}}'
+! stdout panicnil
+stdout randautoseed=0
+
+# Go 1.21 work module should NOT set panicnil=1 in Go 1.20 dependency.
+cp go.mod.21 go.mod
+go list -f '{{.Module.GoVersion}} {{.DefaultGODEBUG}}' q
+! stdout panicnil=1
+! stdout randautoseed
+
+go mod download rsc.io/panicnil # for go.sum
+go list -f '{{.Module.GoVersion}} {{.DefaultGODEBUG}}' rsc.io/panicnil
+! stdout panicnil=1
+! stdout randautoseed
+
+# Go 1.20 work module should set panicnil=1.
+cp go.mod.20 go.mod
+go list -f '{{.Module.GoVersion}} {{.DefaultGODEBUG}}'
+stdout panicnil=1
+stdout randautoseed=0
+
+# Go 1.20 work module should set panicnil=1 in Go 1.20 dependency.
+cp go.mod.20 go.mod
+go list -f '{{.Module.GoVersion}} {{.DefaultGODEBUG}}' q
+stdout panicnil=1
+! stdout randautoseed
+
+# Go 1.21 workspace should leave panicnil with an implicit default.
+cat q/go.mod
+cp go.work.21 go.work
+go list -f '{{.Module.GoVersion}} {{.DefaultGODEBUG}}'
+! stdout panicnil
+stdout randautoseed=0
+rm go.work
+
+# Go 1.20 workspace should set panicnil=1 even in Go 1.21 module.
+cp go.work.20 go.work
+cp go.mod.21 go.mod
+go list -f '{{.Module.GoVersion}} {{.DefaultGODEBUG}}'
+stdout panicnil=1
+stdout randautoseed=0
+rm go.work
+
+[short] skip
+
+# Programs in Go 1.21 work module should trigger run-time error.
+cp go.mod.21 go.mod
+! go run .
+stderr 'panic: panic called with nil argument'
+
+! go run rsc.io/panicnil
+stderr 'panic: panic called with nil argument'
+
+# Programs in Go 1.20 work module use old panic nil behavior.
+cp go.mod.20 go.mod
+! go run .
+stderr 'panic: nil'
+
+! go run rsc.io/panicnil
+stderr 'panic: nil'
+
+# Programs in no module at all should use their go.mod file.
+rm go.mod
+! go run rsc.io/panicnil@v1.0.0
+stderr 'panic: nil'
+
+rm go.mod
+! go run rsc.io/panicnil@v1.1.0
+stderr 'panic: panic called with nil argument'
+
+-- go.work.21 --
+go 1.21
+use .
+use ./q
+
+-- go.work.20 --
+go 1.20
+use .
+use ./q
+
+-- go.mod.21 --
+go 1.21
+module m
+require q v1.0.0
+replace q => ./q
+require rsc.io/panicnil v1.0.0
+
+-- go.mod.20 --
+go 1.20
+module m
+require q v1.0.0
+replace q => ./q
+require rsc.io/panicnil v1.0.0
+
+-- p.go --
+//go:debug randautoseed=0
+
+package main
+
+func main() {
+ panic(nil)
+}
+
+-- q/go.mod --
+go 1.20
+module q
+
+-- q/q.go --
+package main
+func main() {}
! go test -run=FuzzWithNilPanic -fuzz=FuzzWithNilPanic -fuzztime=100x -fuzzminimizetime=1000x
stdout 'testdata[/\\]fuzz[/\\]FuzzWithNilPanic[/\\]'
-stdout 'panic called with nil argument'
+stdout 'panic called with nil argument|test executed panic.nil. or runtime.Goexit'
go run check_testdata.go FuzzWithNilPanic
! go test -run=FuzzWithGoexit -fuzz=FuzzWithGoexit -fuzztime=100x -fuzzminimizetime=1000x
! go list .
-stderr '^go: cannot load module listed in go\.work file: open .+go\.mod:'
+stderr '^go: cannot load module y.go.mod listed in go\.work file: open .+go\.mod:'
-- go.work --
use ./y