mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-26 07:50:26 +08:00
libgo: update to Go1.18rc1 release
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/386594
This commit is contained in:
parent
1931cbad49
commit
20a33efdf3
@ -1,4 +1,4 @@
|
||||
90ed127ef053b758288af9c4e43473e257770bc3
|
||||
fade776395ffe5497d8aae5c0e6bd6d15e09e04a
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
@ -1,4 +1,4 @@
|
||||
41f485b9a7d8fd647c415be1d11b612063dff21c
|
||||
cb5a598d7f2ebd276686403d141a97c026d33458
|
||||
|
||||
The first line of this file holds the git revision number of the
|
||||
last merge done from the master library sources.
|
||||
|
@ -817,6 +817,7 @@ libgo_go_objs = \
|
||||
syscall/errno.lo \
|
||||
syscall/signame.lo \
|
||||
syscall/wait.lo \
|
||||
runtime/internal/syscall/errno.lo \
|
||||
os/dir_gccgo_c.lo \
|
||||
$(golangorg_x_net_lif_lo) \
|
||||
$(golangorg_x_net_route_lo) \
|
||||
@ -1180,6 +1181,11 @@ syscall/wait.lo: go/syscall/wait.c runtime.inc
|
||||
@$(MKDIR_P) syscall
|
||||
$(LTCOMPILE) -c -o $@ $(srcdir)/go/syscall/wait.c
|
||||
|
||||
# Some runtime/internal/syscall functions are written in C.
|
||||
runtime/internal/syscall/errno.lo: go/runtime/internal/syscall/errno.c runtime.inc
|
||||
@$(MKDIR_P) runtime/internal/syscall
|
||||
$(LTCOMPILE) -c -o $@ $(srcdir)/go/runtime/internal/syscall/errno.c
|
||||
|
||||
# An os function is written in C.
|
||||
os/dir_gccgo_c.lo: go/os/dir_gccgo_c.c runtime.inc
|
||||
@$(MKDIR_P) os
|
||||
|
@ -225,7 +225,8 @@ LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
|
||||
am__DEPENDENCIES_3 = $(addsuffix .lo,$(PACKAGES)) \
|
||||
internal/bytealg/bytealg.lo reflect/makefunc_ffi_c.lo \
|
||||
$(am__DEPENDENCIES_1) syscall/errno.lo syscall/signame.lo \
|
||||
syscall/wait.lo os/dir_gccgo_c.lo $(golangorg_x_net_lif_lo) \
|
||||
syscall/wait.lo runtime/internal/syscall/errno.lo \
|
||||
os/dir_gccgo_c.lo $(golangorg_x_net_lif_lo) \
|
||||
$(golangorg_x_net_route_lo) log/syslog/syslog_c.lo \
|
||||
runtime/internal/atomic_c.lo sync/atomic_c.lo \
|
||||
internal/cpu/cpu_gccgo.lo $(am__DEPENDENCIES_2)
|
||||
@ -955,6 +956,7 @@ libgo_go_objs = \
|
||||
syscall/errno.lo \
|
||||
syscall/signame.lo \
|
||||
syscall/wait.lo \
|
||||
runtime/internal/syscall/errno.lo \
|
||||
os/dir_gccgo_c.lo \
|
||||
$(golangorg_x_net_lif_lo) \
|
||||
$(golangorg_x_net_route_lo) \
|
||||
@ -3091,6 +3093,11 @@ syscall/wait.lo: go/syscall/wait.c runtime.inc
|
||||
@$(MKDIR_P) syscall
|
||||
$(LTCOMPILE) -c -o $@ $(srcdir)/go/syscall/wait.c
|
||||
|
||||
# Some runtime/internal/syscall functions are written in C.
|
||||
runtime/internal/syscall/errno.lo: go/runtime/internal/syscall/errno.c runtime.inc
|
||||
@$(MKDIR_P) runtime/internal/syscall
|
||||
$(LTCOMPILE) -c -o $@ $(srcdir)/go/runtime/internal/syscall/errno.c
|
||||
|
||||
# An os function is written in C.
|
||||
os/dir_gccgo_c.lo: go/os/dir_gccgo_c.c runtime.inc
|
||||
@$(MKDIR_P) os
|
||||
|
@ -1 +1 @@
|
||||
go1.18beta2
|
||||
go1.18rc1
|
||||
|
@ -95,11 +95,11 @@ type rune = int32
|
||||
type any = interface{}
|
||||
|
||||
// comparable is an interface that is implemented by all comparable types
|
||||
// (booleans, numbers, strings, pointers, channels, interfaces,
|
||||
// arrays of comparable types, structs whose fields are all comparable types).
|
||||
// (booleans, numbers, strings, pointers, channels, arrays of comparable types,
|
||||
// structs whose fields are all comparable types).
|
||||
// The comparable interface may only be used as a type parameter constraint,
|
||||
// not as the type of a variable.
|
||||
type comparable comparable
|
||||
type comparable interface{ comparable }
|
||||
|
||||
// iota is a predeclared identifier representing the untyped integer ordinal
|
||||
// number of the current const specification in a (usually parenthesized)
|
||||
|
@ -372,6 +372,8 @@ func genSplit(s, sep []byte, sepSave, n int) [][]byte {
|
||||
// n > 0: at most n subslices; the last subslice will be the unsplit remainder.
|
||||
// n == 0: the result is nil (zero subslices)
|
||||
// n < 0: all subslices
|
||||
//
|
||||
// To split around the first instance of a separator, see Cut.
|
||||
func SplitN(s, sep []byte, n int) [][]byte { return genSplit(s, sep, 0, n) }
|
||||
|
||||
// SplitAfterN slices s into subslices after each instance of sep and
|
||||
@ -389,6 +391,8 @@ func SplitAfterN(s, sep []byte, n int) [][]byte {
|
||||
// the subslices between those separators.
|
||||
// If sep is empty, Split splits after each UTF-8 sequence.
|
||||
// It is equivalent to SplitN with a count of -1.
|
||||
//
|
||||
// To split around the first instance of a separator, see Cut.
|
||||
func Split(s, sep []byte) [][]byte { return genSplit(s, sep, 0, -1) }
|
||||
|
||||
// SplitAfter slices s into all subslices after each instance of sep and
|
||||
|
@ -177,14 +177,6 @@
|
||||
// directory, but it is not accessed. When -modfile is specified, an
|
||||
// alternate go.sum file is also used: its path is derived from the
|
||||
// -modfile flag by trimming the ".mod" extension and appending ".sum".
|
||||
// -workfile file
|
||||
// in module aware mode, use the given go.work file as a workspace file.
|
||||
// By default or when -workfile is "auto", the go command searches for a
|
||||
// file named go.work in the current directory and then containing directories
|
||||
// until one is found. If a valid go.work file is found, the modules
|
||||
// specified will collectively be used as the main modules. If -workfile
|
||||
// is "off", or a go.work file is not found in "auto" mode, workspace
|
||||
// mode is disabled.
|
||||
// -overlay file
|
||||
// read a JSON config file that provides an overlay for build operations.
|
||||
// The file is a JSON struct with a single field, named 'Replace', that
|
||||
@ -1379,7 +1371,7 @@
|
||||
// builds from local modules.
|
||||
//
|
||||
// go.work files are line-oriented. Each line holds a single directive,
|
||||
// made up of a keyword followed by aruments. For example:
|
||||
// made up of a keyword followed by arguments. For example:
|
||||
//
|
||||
// go 1.18
|
||||
//
|
||||
@ -1472,19 +1464,14 @@
|
||||
// The -json flag prints the final go.work file in JSON format instead of
|
||||
// writing it back to go.mod. The JSON output corresponds to these Go types:
|
||||
//
|
||||
// type Module struct {
|
||||
// Path string
|
||||
// Version string
|
||||
// }
|
||||
//
|
||||
// type GoWork struct {
|
||||
// Go string
|
||||
// Directory []Directory
|
||||
// Replace []Replace
|
||||
// Go string
|
||||
// Use []Use
|
||||
// Replace []Replace
|
||||
// }
|
||||
//
|
||||
// type Use struct {
|
||||
// Path string
|
||||
// DiskPath string
|
||||
// ModulePath string
|
||||
// }
|
||||
//
|
||||
@ -1493,6 +1480,11 @@
|
||||
// New Module
|
||||
// }
|
||||
//
|
||||
// type Module struct {
|
||||
// Path string
|
||||
// Version string
|
||||
// }
|
||||
//
|
||||
// See the workspaces design proposal at
|
||||
// https://go.googlesource.com/proposal/+/master/design/45713-workspace.md for
|
||||
// more information.
|
||||
@ -2036,6 +2028,8 @@
|
||||
// GOENV
|
||||
// The location of the Go environment configuration file.
|
||||
// Cannot be set using 'go env -w'.
|
||||
// Setting GOENV=off in the environment disables the use of the
|
||||
// default configuration file.
|
||||
// GOFLAGS
|
||||
// A space-separated list of -flag=value settings to apply
|
||||
// to go commands by default, when the given flag is known by
|
||||
@ -2073,6 +2067,14 @@
|
||||
// GOVCS
|
||||
// Lists version control commands that may be used with matching servers.
|
||||
// See 'go help vcs'.
|
||||
// GOWORK
|
||||
// In module aware mode, use the given go.work file as a workspace file.
|
||||
// By default or when GOWORK is "auto", the go command searches for a
|
||||
// file named go.work in the current directory and then containing directories
|
||||
// until one is found. If a valid go.work file is found, the modules
|
||||
// specified will collectively be used as the main modules. If GOWORK
|
||||
// is "off", or a go.work file is not found in "auto" mode, workspace
|
||||
// mode is disabled.
|
||||
//
|
||||
// Environment variables for use with cgo:
|
||||
//
|
||||
|
@ -133,7 +133,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
gotool, err := testenv.GoTool()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, "locating go tool: ", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
|
@ -62,13 +62,6 @@ func AddModFlag(flags *flag.FlagSet) {
|
||||
flags.Var(explicitStringFlag{value: &cfg.BuildMod, explicit: &cfg.BuildModExplicit}, "mod", "")
|
||||
}
|
||||
|
||||
// AddWorkfileFlag adds the workfile flag to the flag set. It enables workspace
|
||||
// mode for commands that support it by resetting the cfg.WorkFile variable
|
||||
// to "" (equivalent to auto) rather than off.
|
||||
func AddWorkfileFlag(flags *flag.FlagSet) {
|
||||
flags.Var(explicitStringFlag{value: &cfg.WorkFile, explicit: &cfg.WorkFileExplicit}, "workfile", "")
|
||||
}
|
||||
|
||||
// AddModCommonFlags adds the module-related flags common to build commands
|
||||
// and 'go mod' subcommands.
|
||||
func AddModCommonFlags(flags *flag.FlagSet) {
|
||||
|
@ -49,10 +49,8 @@ var (
|
||||
BuildWork bool // -work flag
|
||||
BuildX bool // -x flag
|
||||
|
||||
ModCacheRW bool // -modcacherw flag
|
||||
ModFile string // -modfile flag
|
||||
WorkFile string // -workfile flag
|
||||
WorkFileExplicit bool // whether -workfile was set explicitly
|
||||
ModCacheRW bool // -modcacherw flag
|
||||
ModFile string // -modfile flag
|
||||
|
||||
CmdName string // "build", "install", "list", "mod tidy", etc.
|
||||
|
||||
|
@ -154,6 +154,10 @@ func ExtraEnvVars() []cfg.EnvVar {
|
||||
}
|
||||
modload.InitWorkfile()
|
||||
gowork := modload.WorkFilePath()
|
||||
// As a special case, if a user set off explicitly, report that in GOWORK.
|
||||
if cfg.Getenv("GOWORK") == "off" {
|
||||
gowork = "off"
|
||||
}
|
||||
return []cfg.EnvVar{
|
||||
{Name: "GOMOD", Value: gomod},
|
||||
{Name: "GOWORK", Value: gowork},
|
||||
|
@ -506,6 +506,8 @@ General-purpose environment variables:
|
||||
GOENV
|
||||
The location of the Go environment configuration file.
|
||||
Cannot be set using 'go env -w'.
|
||||
Setting GOENV=off in the environment disables the use of the
|
||||
default configuration file.
|
||||
GOFLAGS
|
||||
A space-separated list of -flag=value settings to apply
|
||||
to go commands by default, when the given flag is known by
|
||||
@ -543,6 +545,14 @@ General-purpose environment variables:
|
||||
GOVCS
|
||||
Lists version control commands that may be used with matching servers.
|
||||
See 'go help vcs'.
|
||||
GOWORK
|
||||
In module aware mode, use the given go.work file as a workspace file.
|
||||
By default or when GOWORK is "auto", the go command searches for a
|
||||
file named go.work in the current directory and then containing directories
|
||||
until one is found. If a valid go.work file is found, the modules
|
||||
specified will collectively be used as the main modules. If GOWORK
|
||||
is "off", or a go.work file is not found in "auto" mode, workspace
|
||||
mode is disabled.
|
||||
|
||||
Environment variables for use with cgo:
|
||||
|
||||
|
@ -316,7 +316,6 @@ For more about modules, see https://golang.org/ref/mod.
|
||||
func init() {
|
||||
CmdList.Run = runList // break init cycle
|
||||
work.AddBuildFlags(CmdList, work.DefaultBuildFlags)
|
||||
base.AddWorkfileFlag(&CmdList.Flag)
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -819,11 +819,11 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo
|
||||
}
|
||||
r := resolvedImportCache.Do(importKey, func() any {
|
||||
var r resolvedImport
|
||||
if build.IsLocalImport(path) {
|
||||
if cfg.ModulesEnabled {
|
||||
r.dir, r.path, r.err = modload.Lookup(parentPath, parentIsStd, path)
|
||||
} else if build.IsLocalImport(path) {
|
||||
r.dir = filepath.Join(parentDir, path)
|
||||
r.path = dirToImportPath(r.dir)
|
||||
} else if cfg.ModulesEnabled {
|
||||
r.dir, r.path, r.err = modload.Lookup(parentPath, parentIsStd, path)
|
||||
} else if mode&ResolveImport != 0 {
|
||||
// We do our own path resolution, because we want to
|
||||
// find out the key to use in packageCache without the
|
||||
@ -1113,6 +1113,7 @@ func dirAndRoot(path string, dir, root string) (string, string) {
|
||||
}
|
||||
|
||||
if !str.HasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator || path != "command-line-arguments" && !build.IsLocalImport(path) && filepath.Join(root, path) != dir {
|
||||
debug.PrintStack()
|
||||
base.Fatalf("unexpected directory layout:\n"+
|
||||
" import path: %s\n"+
|
||||
" root: %s\n"+
|
||||
@ -2235,13 +2236,17 @@ func (p *Package) setBuildInfo() {
|
||||
|
||||
var debugModFromModinfo func(*modinfo.ModulePublic) *debug.Module
|
||||
debugModFromModinfo = func(mi *modinfo.ModulePublic) *debug.Module {
|
||||
version := mi.Version
|
||||
if version == "" {
|
||||
version = "(devel)"
|
||||
}
|
||||
dm := &debug.Module{
|
||||
Path: mi.Path,
|
||||
Version: mi.Version,
|
||||
Version: version,
|
||||
}
|
||||
if mi.Replace != nil {
|
||||
dm.Replace = debugModFromModinfo(mi.Replace)
|
||||
} else {
|
||||
} else if mi.Version != "" {
|
||||
dm.Sum = modfetch.Sum(module.Version{Path: mi.Path, Version: mi.Version})
|
||||
}
|
||||
return dm
|
||||
@ -2424,12 +2429,7 @@ func (p *Package) setBuildInfo() {
|
||||
appendSetting("vcs.modified", strconv.FormatBool(st.Uncommitted))
|
||||
}
|
||||
|
||||
text, err := info.MarshalText()
|
||||
if err != nil {
|
||||
setPkgErrorf("error formatting build info: %v", err)
|
||||
return
|
||||
}
|
||||
p.Internal.BuildInfo = string(text)
|
||||
p.Internal.BuildInfo = info.String()
|
||||
}
|
||||
|
||||
// SafeArg reports whether arg is a "safe" command-line argument,
|
||||
|
@ -70,7 +70,6 @@ func init() {
|
||||
// TODO(jayconrod): https://golang.org/issue/35849 Apply -x to other 'go mod' commands.
|
||||
cmdDownload.Flag.BoolVar(&cfg.BuildX, "x", false, "")
|
||||
base.AddModCommonFlags(&cmdDownload.Flag)
|
||||
base.AddWorkfileFlag(&cmdDownload.Flag)
|
||||
}
|
||||
|
||||
type moduleJSON struct {
|
||||
|
@ -42,7 +42,6 @@ var (
|
||||
func init() {
|
||||
cmdGraph.Flag.Var(&graphGo, "go", "")
|
||||
base.AddModCommonFlags(&cmdGraph.Flag)
|
||||
base.AddWorkfileFlag(&cmdGraph.Flag)
|
||||
}
|
||||
|
||||
func runGraph(ctx context.Context, cmd *base.Command, args []string) {
|
||||
|
@ -39,7 +39,6 @@ See https://golang.org/ref/mod#go-mod-verify for more about 'go mod verify'.
|
||||
|
||||
func init() {
|
||||
base.AddModCommonFlags(&cmdVerify.Flag)
|
||||
base.AddWorkfileFlag(&cmdVerify.Flag)
|
||||
}
|
||||
|
||||
func runVerify(ctx context.Context, cmd *base.Command, args []string) {
|
||||
|
@ -59,7 +59,6 @@ var (
|
||||
func init() {
|
||||
cmdWhy.Run = runWhy // break init cycle
|
||||
base.AddModCommonFlags(&cmdWhy.Flag)
|
||||
base.AddWorkfileFlag(&cmdWhy.Flag)
|
||||
}
|
||||
|
||||
func runWhy(ctx context.Context, cmd *base.Command, args []string) {
|
||||
|
@ -298,16 +298,13 @@ func (r *codeRepo) Latest() (*RevInfo, error) {
|
||||
// If statVers is a valid module version, it is used for the Version field.
|
||||
// Otherwise, the Version is derived from the passed-in info and recent tags.
|
||||
func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, error) {
|
||||
info2 := &RevInfo{
|
||||
Name: info.Name,
|
||||
Short: info.Short,
|
||||
Time: info.Time,
|
||||
}
|
||||
|
||||
// If this is a plain tag (no dir/ prefix)
|
||||
// and the module path is unversioned,
|
||||
// and if the underlying file tree has no go.mod,
|
||||
// then allow using the tag with a +incompatible suffix.
|
||||
//
|
||||
// (If the version is +incompatible, then the go.mod file must not exist:
|
||||
// +incompatible is not an ongoing opt-out from semantic import versioning.)
|
||||
var canUseIncompatible func() bool
|
||||
canUseIncompatible = func() bool {
|
||||
var ok bool
|
||||
@ -321,19 +318,12 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
||||
return ok
|
||||
}
|
||||
|
||||
invalidf := func(format string, args ...any) error {
|
||||
return &module.ModuleError{
|
||||
Path: r.modPath,
|
||||
Err: &module.InvalidVersionError{
|
||||
Version: info2.Version,
|
||||
Err: fmt.Errorf(format, args...),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// checkGoMod verifies that the go.mod file for the module exists or does not
|
||||
// exist as required by info2.Version and the module path represented by r.
|
||||
checkGoMod := func() (*RevInfo, error) {
|
||||
// checkCanonical verifies that the canonical version v is compatible with the
|
||||
// module path represented by r, adding a "+incompatible" suffix if needed.
|
||||
//
|
||||
// If statVers is also canonical, checkCanonical also verifies that v is
|
||||
// either statVers or statVers with the added "+incompatible" suffix.
|
||||
checkCanonical := func(v string) (*RevInfo, error) {
|
||||
// If r.codeDir is non-empty, then the go.mod file must exist: the module
|
||||
// author — not the module consumer, — gets to decide how to carve up the repo
|
||||
// into modules.
|
||||
@ -344,73 +334,91 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
||||
// r.findDir verifies both of these conditions. Execute it now so that
|
||||
// r.Stat will correctly return a notExistError if the go.mod location or
|
||||
// declared module path doesn't match.
|
||||
_, _, _, err := r.findDir(info2.Version)
|
||||
_, _, _, err := r.findDir(v)
|
||||
if err != nil {
|
||||
// TODO: It would be nice to return an error like "not a module".
|
||||
// Right now we return "missing go.mod", which is a little confusing.
|
||||
return nil, &module.ModuleError{
|
||||
Path: r.modPath,
|
||||
Err: &module.InvalidVersionError{
|
||||
Version: info2.Version,
|
||||
Version: v,
|
||||
Err: notExistError{err: err},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If the version is +incompatible, then the go.mod file must not exist:
|
||||
// +incompatible is not an ongoing opt-out from semantic import versioning.
|
||||
if strings.HasSuffix(info2.Version, "+incompatible") {
|
||||
if !canUseIncompatible() {
|
||||
if r.pathMajor != "" {
|
||||
return nil, invalidf("+incompatible suffix not allowed: module path includes a major version suffix, so major version must match")
|
||||
} else {
|
||||
return nil, invalidf("+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required")
|
||||
}
|
||||
}
|
||||
|
||||
if err := module.CheckPathMajor(strings.TrimSuffix(info2.Version, "+incompatible"), r.pathMajor); err == nil {
|
||||
return nil, invalidf("+incompatible suffix not allowed: major version %s is compatible", semver.Major(info2.Version))
|
||||
invalidf := func(format string, args ...any) error {
|
||||
return &module.ModuleError{
|
||||
Path: r.modPath,
|
||||
Err: &module.InvalidVersionError{
|
||||
Version: v,
|
||||
Err: fmt.Errorf(format, args...),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return info2, nil
|
||||
// Add the +incompatible suffix if needed or requested explicitly, and
|
||||
// verify that its presence or absence is appropriate for this version
|
||||
// (which depends on whether it has an explicit go.mod file).
|
||||
|
||||
if v == strings.TrimSuffix(statVers, "+incompatible") {
|
||||
v = statVers
|
||||
}
|
||||
base := strings.TrimSuffix(v, "+incompatible")
|
||||
var errIncompatible error
|
||||
if !module.MatchPathMajor(base, r.pathMajor) {
|
||||
if canUseIncompatible() {
|
||||
v = base + "+incompatible"
|
||||
} else {
|
||||
if r.pathMajor != "" {
|
||||
errIncompatible = invalidf("module path includes a major version suffix, so major version must match")
|
||||
} else {
|
||||
errIncompatible = invalidf("module contains a go.mod file, so module path must match major version (%q)", path.Join(r.pathPrefix, semver.Major(v)))
|
||||
}
|
||||
}
|
||||
} else if strings.HasSuffix(v, "+incompatible") {
|
||||
errIncompatible = invalidf("+incompatible suffix not allowed: major version %s is compatible", semver.Major(v))
|
||||
}
|
||||
|
||||
if statVers != "" && statVers == module.CanonicalVersion(statVers) {
|
||||
// Since the caller-requested version is canonical, it would be very
|
||||
// confusing to resolve it to anything but itself, possibly with a
|
||||
// "+incompatible" suffix. Error out explicitly.
|
||||
if statBase := strings.TrimSuffix(statVers, "+incompatible"); statBase != base {
|
||||
return nil, &module.ModuleError{
|
||||
Path: r.modPath,
|
||||
Err: &module.InvalidVersionError{
|
||||
Version: statVers,
|
||||
Err: fmt.Errorf("resolves to version %v (%s is not a tag)", v, statBase),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if errIncompatible != nil {
|
||||
return nil, errIncompatible
|
||||
}
|
||||
|
||||
return &RevInfo{
|
||||
Name: info.Name,
|
||||
Short: info.Short,
|
||||
Time: info.Time,
|
||||
Version: v,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Determine version.
|
||||
//
|
||||
// If statVers is canonical, then the original call was repo.Stat(statVers).
|
||||
// Since the version is canonical, we must not resolve it to anything but
|
||||
// itself, possibly with a '+incompatible' annotation: we do not need to do
|
||||
// the work required to look for an arbitrary pseudo-version.
|
||||
if statVers != "" && statVers == module.CanonicalVersion(statVers) {
|
||||
info2.Version = statVers
|
||||
|
||||
if module.IsPseudoVersion(info2.Version) {
|
||||
if err := r.validatePseudoVersion(info, info2.Version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return checkGoMod()
|
||||
if module.IsPseudoVersion(statVers) {
|
||||
if err := r.validatePseudoVersion(info, statVers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := module.CheckPathMajor(info2.Version, r.pathMajor); err != nil {
|
||||
if canUseIncompatible() {
|
||||
info2.Version += "+incompatible"
|
||||
return checkGoMod()
|
||||
} else {
|
||||
if vErr, ok := err.(*module.InvalidVersionError); ok {
|
||||
// We're going to describe why the version is invalid in more detail,
|
||||
// so strip out the existing “invalid version” wrapper.
|
||||
err = vErr.Err
|
||||
}
|
||||
return nil, invalidf("module contains a go.mod file, so major version must be compatible: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return checkGoMod()
|
||||
return checkCanonical(statVers)
|
||||
}
|
||||
|
||||
// statVers is empty or non-canonical, so we need to resolve it to a canonical
|
||||
// version or pseudo-version.
|
||||
// statVers is not a pseudo-version, so we need to either resolve it to a
|
||||
// canonical version or verify that it is already a canonical tag
|
||||
// (not a branch).
|
||||
|
||||
// Derive or verify a version from a code repo tag.
|
||||
// Tag must have a prefix matching codeDir.
|
||||
@ -441,71 +449,62 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
||||
if v == "" || !strings.HasPrefix(trimmed, v) {
|
||||
return "", false // Invalid or incomplete version (just vX or vX.Y).
|
||||
}
|
||||
if isRetracted(v) {
|
||||
return "", false
|
||||
}
|
||||
if v == trimmed {
|
||||
tagIsCanonical = true
|
||||
}
|
||||
|
||||
if err := module.CheckPathMajor(v, r.pathMajor); err != nil {
|
||||
if canUseIncompatible() {
|
||||
return v + "+incompatible", tagIsCanonical
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
return v, tagIsCanonical
|
||||
}
|
||||
|
||||
// If the VCS gave us a valid version, use that.
|
||||
if v, tagIsCanonical := tagToVersion(info.Version); tagIsCanonical {
|
||||
info2.Version = v
|
||||
return checkGoMod()
|
||||
if info, err := checkCanonical(v); err == nil {
|
||||
return info, err
|
||||
}
|
||||
}
|
||||
|
||||
// Look through the tags on the revision for either a usable canonical version
|
||||
// or an appropriate base for a pseudo-version.
|
||||
var pseudoBase string
|
||||
var (
|
||||
highestCanonical string
|
||||
pseudoBase string
|
||||
)
|
||||
for _, pathTag := range info.Tags {
|
||||
v, tagIsCanonical := tagToVersion(pathTag)
|
||||
if tagIsCanonical {
|
||||
if statVers != "" && semver.Compare(v, statVers) == 0 {
|
||||
// The user requested a non-canonical version, but the tag for the
|
||||
// canonical equivalent refers to the same revision. Use it.
|
||||
info2.Version = v
|
||||
return checkGoMod()
|
||||
if statVers != "" && semver.Compare(v, statVers) == 0 {
|
||||
// The tag is equivalent to the version requested by the user.
|
||||
if tagIsCanonical {
|
||||
// This tag is the canonical form of the requested version,
|
||||
// not some other form with extra build metadata.
|
||||
// Use this tag so that the resolved version will match exactly.
|
||||
// (If it isn't actually allowed, we'll error out in checkCanonical.)
|
||||
return checkCanonical(v)
|
||||
} else {
|
||||
// Save the highest canonical tag for the revision. If we don't find a
|
||||
// better match, we'll use it as the canonical version.
|
||||
// The user explicitly requested something equivalent to this tag. We
|
||||
// can't use the version from the tag directly: since the tag is not
|
||||
// canonical, it could be ambiguous. For example, tags v0.0.1+a and
|
||||
// v0.0.1+b might both exist and refer to different revisions.
|
||||
//
|
||||
// NOTE: Do not replace this with semver.Max. Despite the name,
|
||||
// semver.Max *also* canonicalizes its arguments, which uses
|
||||
// semver.Canonical instead of module.CanonicalVersion and thereby
|
||||
// strips our "+incompatible" suffix.
|
||||
if semver.Compare(info2.Version, v) < 0 {
|
||||
info2.Version = v
|
||||
}
|
||||
// The tag is otherwise valid for the module, so we can at least use it as
|
||||
// the base of an unambiguous pseudo-version.
|
||||
//
|
||||
// If multiple tags match, tagToVersion will canonicalize them to the same
|
||||
// base version.
|
||||
pseudoBase = v
|
||||
}
|
||||
}
|
||||
// Save the highest non-retracted canonical tag for the revision.
|
||||
// If we don't find a better match, we'll use it as the canonical version.
|
||||
if tagIsCanonical && semver.Compare(highestCanonical, v) < 0 && !isRetracted(v) {
|
||||
if module.MatchPathMajor(v, r.pathMajor) || canUseIncompatible() {
|
||||
highestCanonical = v
|
||||
}
|
||||
} else if v != "" && semver.Compare(v, statVers) == 0 {
|
||||
// The user explicitly requested something equivalent to this tag. We
|
||||
// can't use the version from the tag directly: since the tag is not
|
||||
// canonical, it could be ambiguous. For example, tags v0.0.1+a and
|
||||
// v0.0.1+b might both exist and refer to different revisions.
|
||||
//
|
||||
// The tag is otherwise valid for the module, so we can at least use it as
|
||||
// the base of an unambiguous pseudo-version.
|
||||
//
|
||||
// If multiple tags match, tagToVersion will canonicalize them to the same
|
||||
// base version.
|
||||
pseudoBase = v
|
||||
}
|
||||
}
|
||||
|
||||
// If we found any canonical tag for the revision, return it.
|
||||
// If we found a valid canonical tag for the revision, return it.
|
||||
// Even if we found a good pseudo-version base, a canonical version is better.
|
||||
if info2.Version != "" {
|
||||
return checkGoMod()
|
||||
if highestCanonical != "" {
|
||||
return checkCanonical(highestCanonical)
|
||||
}
|
||||
|
||||
// Find the highest tagged version in the revision's history, subject to
|
||||
@ -528,11 +527,10 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
||||
tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor("v0"))
|
||||
}
|
||||
}
|
||||
pseudoBase, _ = tagToVersion(tag) // empty if the tag is invalid
|
||||
pseudoBase, _ = tagToVersion(tag)
|
||||
}
|
||||
|
||||
info2.Version = module.PseudoVersion(r.pseudoMajor, pseudoBase, info.Time, info.Short)
|
||||
return checkGoMod()
|
||||
return checkCanonical(module.PseudoVersion(r.pseudoMajor, pseudoBase, info.Time, info.Short))
|
||||
}
|
||||
|
||||
// validatePseudoVersion checks that version has a major version compatible with
|
||||
@ -556,10 +554,6 @@ func (r *codeRepo) validatePseudoVersion(info *codehost.RevInfo, version string)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := module.CheckPathMajor(version, r.pathMajor); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rev, err := module.PseudoVersionRev(version)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -418,171 +418,204 @@ var codeRepoTests = []codeRepoTest{
|
||||
zipSum: "h1:JItBZ+gwA5WvtZEGEbuDL4lUttGtLrs53lmdurq3bOg=",
|
||||
zipFileHash: "9ea9ae1673cffcc44b7fdd3cc89953d68c102449b46c982dbf085e4f2e394da5",
|
||||
},
|
||||
{
|
||||
// Git branch with a semver name, +incompatible version, and no go.mod file.
|
||||
vcs: "git",
|
||||
path: "vcs-test.golang.org/go/mod/gitrepo1",
|
||||
rev: "v2.3.4+incompatible",
|
||||
err: `resolves to version v2.0.1+incompatible (v2.3.4 is not a tag)`,
|
||||
},
|
||||
{
|
||||
// Git branch with a semver name, matching go.mod file, and compatible version.
|
||||
vcs: "git",
|
||||
path: "vcs-test.golang.org/git/semver-branch.git",
|
||||
rev: "v1.0.0",
|
||||
err: `resolves to version v0.1.1-0.20220202191944-09c4d8f6938c (v1.0.0 is not a tag)`,
|
||||
},
|
||||
{
|
||||
// Git branch with a semver name, matching go.mod file, and disallowed +incompatible version.
|
||||
// The version/tag mismatch takes precedence over the +incompatible mismatched.
|
||||
vcs: "git",
|
||||
path: "vcs-test.golang.org/git/semver-branch.git",
|
||||
rev: "v2.0.0+incompatible",
|
||||
err: `resolves to version v0.1.0 (v2.0.0 is not a tag)`,
|
||||
},
|
||||
{
|
||||
// Git branch with a semver name, matching go.mod file, and mismatched version.
|
||||
// The version/tag mismatch takes precedence over the +incompatible mismatched.
|
||||
vcs: "git",
|
||||
path: "vcs-test.golang.org/git/semver-branch.git",
|
||||
rev: "v2.0.0",
|
||||
err: `resolves to version v0.1.0 (v2.0.0 is not a tag)`,
|
||||
},
|
||||
{
|
||||
// v3.0.0-devel is the same as tag v4.0.0-beta.1, but v4.0.0-beta.1 would
|
||||
// not be allowed because it is incompatible and a go.mod file exists.
|
||||
// The error message should refer to a valid pseudo-version, not the
|
||||
// unusable semver tag.
|
||||
vcs: "git",
|
||||
path: "vcs-test.golang.org/git/semver-branch.git",
|
||||
rev: "v3.0.0-devel",
|
||||
err: `resolves to version v0.1.1-0.20220203155313-d59622f6e4d7 (v3.0.0-devel is not a tag)`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestCodeRepo(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
tmpdir := t.TempDir()
|
||||
|
||||
tmpdir, err := os.MkdirTemp("", "modfetch-test-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
for _, tt := range codeRepoTests {
|
||||
f := func(tt codeRepoTest) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if tt.vcs != "mod" {
|
||||
testenv.MustHaveExecPath(t, tt.vcs)
|
||||
}
|
||||
|
||||
t.Run("parallel", func(t *testing.T) {
|
||||
for _, tt := range codeRepoTests {
|
||||
f := func(tt codeRepoTest) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if tt.vcs != "mod" {
|
||||
testenv.MustHaveExecPath(t, tt.vcs)
|
||||
}
|
||||
repo := Lookup("direct", tt.path)
|
||||
|
||||
repo := Lookup("direct", tt.path)
|
||||
if tt.mpath == "" {
|
||||
tt.mpath = tt.path
|
||||
}
|
||||
if mpath := repo.ModulePath(); mpath != tt.mpath {
|
||||
t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath)
|
||||
}
|
||||
|
||||
if tt.mpath == "" {
|
||||
tt.mpath = tt.path
|
||||
}
|
||||
if mpath := repo.ModulePath(); mpath != tt.mpath {
|
||||
t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath)
|
||||
}
|
||||
|
||||
info, err := repo.Stat(tt.rev)
|
||||
if err != nil {
|
||||
if tt.err != "" {
|
||||
if !strings.Contains(err.Error(), tt.err) {
|
||||
t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err)
|
||||
}
|
||||
return
|
||||
}
|
||||
t.Fatalf("repo.Stat(%q): %v", tt.rev, err)
|
||||
}
|
||||
info, err := repo.Stat(tt.rev)
|
||||
if err != nil {
|
||||
if tt.err != "" {
|
||||
t.Errorf("repo.Stat(%q): success, wanted error", tt.rev)
|
||||
}
|
||||
if info.Version != tt.version {
|
||||
t.Errorf("info.Version = %q, want %q", info.Version, tt.version)
|
||||
}
|
||||
if info.Name != tt.name {
|
||||
t.Errorf("info.Name = %q, want %q", info.Name, tt.name)
|
||||
}
|
||||
if info.Short != tt.short {
|
||||
t.Errorf("info.Short = %q, want %q", info.Short, tt.short)
|
||||
}
|
||||
if !info.Time.Equal(tt.time) {
|
||||
t.Errorf("info.Time = %v, want %v", info.Time, tt.time)
|
||||
if !strings.Contains(err.Error(), tt.err) {
|
||||
t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err)
|
||||
}
|
||||
return
|
||||
}
|
||||
t.Fatalf("repo.Stat(%q): %v", tt.rev, err)
|
||||
}
|
||||
if tt.err != "" {
|
||||
t.Errorf("repo.Stat(%q): success, wanted error", tt.rev)
|
||||
}
|
||||
if info.Version != tt.version {
|
||||
t.Errorf("info.Version = %q, want %q", info.Version, tt.version)
|
||||
}
|
||||
if info.Name != tt.name {
|
||||
t.Errorf("info.Name = %q, want %q", info.Name, tt.name)
|
||||
}
|
||||
if info.Short != tt.short {
|
||||
t.Errorf("info.Short = %q, want %q", info.Short, tt.short)
|
||||
}
|
||||
if !info.Time.Equal(tt.time) {
|
||||
t.Errorf("info.Time = %v, want %v", info.Time, tt.time)
|
||||
}
|
||||
|
||||
if tt.gomod != "" || tt.gomodErr != "" {
|
||||
data, err := repo.GoMod(tt.version)
|
||||
if err != nil && tt.gomodErr == "" {
|
||||
t.Errorf("repo.GoMod(%q): %v", tt.version, err)
|
||||
} else if err != nil && tt.gomodErr != "" {
|
||||
if err.Error() != tt.gomodErr {
|
||||
t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomodErr)
|
||||
}
|
||||
} else if tt.gomodErr != "" {
|
||||
t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomodErr)
|
||||
} else if string(data) != tt.gomod {
|
||||
t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod)
|
||||
}
|
||||
}
|
||||
|
||||
needHash := !testing.Short() && (tt.zipFileHash != "" || tt.zipSum != "")
|
||||
if tt.zip != nil || tt.zipErr != "" || needHash {
|
||||
f, err := os.CreateTemp(tmpdir, tt.version+".zip.")
|
||||
if err != nil {
|
||||
t.Fatalf("os.CreateTemp: %v", err)
|
||||
}
|
||||
zipfile := f.Name()
|
||||
defer func() {
|
||||
f.Close()
|
||||
os.Remove(zipfile)
|
||||
}()
|
||||
|
||||
var w io.Writer
|
||||
var h hash.Hash
|
||||
if needHash {
|
||||
h = sha256.New()
|
||||
w = io.MultiWriter(f, h)
|
||||
} else {
|
||||
w = f
|
||||
}
|
||||
err = repo.Zip(w, tt.version)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
if tt.zipErr != "" {
|
||||
if err.Error() == tt.zipErr {
|
||||
return
|
||||
}
|
||||
t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.zipErr)
|
||||
}
|
||||
t.Fatalf("repo.Zip(%q): %v", tt.version, err)
|
||||
}
|
||||
if tt.zipErr != "" {
|
||||
t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.zipErr)
|
||||
}
|
||||
|
||||
if tt.zip != nil {
|
||||
prefix := tt.path + "@" + tt.version + "/"
|
||||
z, err := zip.OpenReader(zipfile)
|
||||
if err != nil {
|
||||
t.Fatalf("open zip %s: %v", zipfile, err)
|
||||
}
|
||||
var names []string
|
||||
for _, file := range z.File {
|
||||
if !strings.HasPrefix(file.Name, prefix) {
|
||||
t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix)
|
||||
continue
|
||||
}
|
||||
names = append(names, file.Name[len(prefix):])
|
||||
}
|
||||
z.Close()
|
||||
if !reflect.DeepEqual(names, tt.zip) {
|
||||
t.Fatalf("zip = %v\nwant %v\n", names, tt.zip)
|
||||
}
|
||||
}
|
||||
|
||||
if needHash {
|
||||
sum, err := dirhash.HashZip(zipfile, dirhash.Hash1)
|
||||
if err != nil {
|
||||
t.Errorf("repo.Zip(%q): %v", tt.version, err)
|
||||
} else if sum != tt.zipSum {
|
||||
t.Errorf("repo.Zip(%q): got file with sum %q, want %q", tt.version, sum, tt.zipSum)
|
||||
} else if zipFileHash := hex.EncodeToString(h.Sum(nil)); zipFileHash != tt.zipFileHash {
|
||||
t.Errorf("repo.Zip(%q): got file with hash %q, want %q (but content has correct sum)", tt.version, zipFileHash, tt.zipFileHash)
|
||||
}
|
||||
if tt.gomod != "" || tt.gomodErr != "" {
|
||||
data, err := repo.GoMod(tt.version)
|
||||
if err != nil && tt.gomodErr == "" {
|
||||
t.Errorf("repo.GoMod(%q): %v", tt.version, err)
|
||||
} else if err != nil && tt.gomodErr != "" {
|
||||
if err.Error() != tt.gomodErr {
|
||||
t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomodErr)
|
||||
}
|
||||
} else if tt.gomodErr != "" {
|
||||
t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomodErr)
|
||||
} else if string(data) != tt.gomod {
|
||||
t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod)
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f(tt))
|
||||
if strings.HasPrefix(tt.path, vgotest1git) {
|
||||
for vcs, alt := range altVgotests {
|
||||
altTest := tt
|
||||
altTest.vcs = vcs
|
||||
altTest.path = alt + strings.TrimPrefix(altTest.path, vgotest1git)
|
||||
if strings.HasPrefix(altTest.mpath, vgotest1git) {
|
||||
altTest.mpath = alt + strings.TrimPrefix(altTest.mpath, vgotest1git)
|
||||
|
||||
needHash := !testing.Short() && (tt.zipFileHash != "" || tt.zipSum != "")
|
||||
if tt.zip != nil || tt.zipErr != "" || needHash {
|
||||
f, err := os.CreateTemp(tmpdir, tt.version+".zip.")
|
||||
if err != nil {
|
||||
t.Fatalf("os.CreateTemp: %v", err)
|
||||
}
|
||||
var m map[string]string
|
||||
if alt == vgotest1hg {
|
||||
m = hgmap
|
||||
zipfile := f.Name()
|
||||
defer func() {
|
||||
f.Close()
|
||||
os.Remove(zipfile)
|
||||
}()
|
||||
|
||||
var w io.Writer
|
||||
var h hash.Hash
|
||||
if needHash {
|
||||
h = sha256.New()
|
||||
w = io.MultiWriter(f, h)
|
||||
} else {
|
||||
w = f
|
||||
}
|
||||
err = repo.Zip(w, tt.version)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
if tt.zipErr != "" {
|
||||
if err.Error() == tt.zipErr {
|
||||
return
|
||||
}
|
||||
t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.zipErr)
|
||||
}
|
||||
t.Fatalf("repo.Zip(%q): %v", tt.version, err)
|
||||
}
|
||||
if tt.zipErr != "" {
|
||||
t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.zipErr)
|
||||
}
|
||||
|
||||
if tt.zip != nil {
|
||||
prefix := tt.path + "@" + tt.version + "/"
|
||||
z, err := zip.OpenReader(zipfile)
|
||||
if err != nil {
|
||||
t.Fatalf("open zip %s: %v", zipfile, err)
|
||||
}
|
||||
var names []string
|
||||
for _, file := range z.File {
|
||||
if !strings.HasPrefix(file.Name, prefix) {
|
||||
t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix)
|
||||
continue
|
||||
}
|
||||
names = append(names, file.Name[len(prefix):])
|
||||
}
|
||||
z.Close()
|
||||
if !reflect.DeepEqual(names, tt.zip) {
|
||||
t.Fatalf("zip = %v\nwant %v\n", names, tt.zip)
|
||||
}
|
||||
}
|
||||
|
||||
if needHash {
|
||||
sum, err := dirhash.HashZip(zipfile, dirhash.Hash1)
|
||||
if err != nil {
|
||||
t.Errorf("repo.Zip(%q): %v", tt.version, err)
|
||||
} else if sum != tt.zipSum {
|
||||
t.Errorf("repo.Zip(%q): got file with sum %q, want %q", tt.version, sum, tt.zipSum)
|
||||
} else if zipFileHash := hex.EncodeToString(h.Sum(nil)); zipFileHash != tt.zipFileHash {
|
||||
t.Errorf("repo.Zip(%q): got file with hash %q, want %q (but content has correct sum)", tt.version, zipFileHash, tt.zipFileHash)
|
||||
}
|
||||
}
|
||||
altTest.version = remap(altTest.version, m)
|
||||
altTest.name = remap(altTest.name, m)
|
||||
altTest.short = remap(altTest.short, m)
|
||||
altTest.rev = remap(altTest.rev, m)
|
||||
altTest.err = remap(altTest.err, m)
|
||||
altTest.gomodErr = remap(altTest.gomodErr, m)
|
||||
altTest.zipErr = remap(altTest.zipErr, m)
|
||||
altTest.zipSum = ""
|
||||
altTest.zipFileHash = ""
|
||||
t.Run(strings.ReplaceAll(altTest.path, "/", "_")+"/"+altTest.rev, f(altTest))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f(tt))
|
||||
if strings.HasPrefix(tt.path, vgotest1git) {
|
||||
for vcs, alt := range altVgotests {
|
||||
altTest := tt
|
||||
altTest.vcs = vcs
|
||||
altTest.path = alt + strings.TrimPrefix(altTest.path, vgotest1git)
|
||||
if strings.HasPrefix(altTest.mpath, vgotest1git) {
|
||||
altTest.mpath = alt + strings.TrimPrefix(altTest.mpath, vgotest1git)
|
||||
}
|
||||
var m map[string]string
|
||||
if alt == vgotest1hg {
|
||||
m = hgmap
|
||||
}
|
||||
altTest.version = remap(altTest.version, m)
|
||||
altTest.name = remap(altTest.name, m)
|
||||
altTest.short = remap(altTest.short, m)
|
||||
altTest.rev = remap(altTest.rev, m)
|
||||
altTest.err = remap(altTest.err, m)
|
||||
altTest.gomodErr = remap(altTest.gomodErr, m)
|
||||
altTest.zipErr = remap(altTest.zipErr, m)
|
||||
altTest.zipSum = ""
|
||||
altTest.zipFileHash = ""
|
||||
t.Run(strings.ReplaceAll(altTest.path, "/", "_")+"/"+altTest.rev, f(altTest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var hgmap = map[string]string{
|
||||
|
@ -319,7 +319,7 @@ func downloadZip(ctx context.Context, mod module.Version, zipfile string) (err e
|
||||
//
|
||||
// If the hash does not match go.sum (or the sumdb if enabled), hashZip returns
|
||||
// an error and does not write ziphashfile.
|
||||
func hashZip(mod module.Version, zipfile, ziphashfile string) error {
|
||||
func hashZip(mod module.Version, zipfile, ziphashfile string) (err error) {
|
||||
hash, err := dirhash.HashZip(zipfile, dirhash.DefaultHash)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -331,16 +331,17 @@ func hashZip(mod module.Version, zipfile, ziphashfile string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if closeErr := hf.Close(); err == nil && closeErr != nil {
|
||||
err = closeErr
|
||||
}
|
||||
}()
|
||||
if err := hf.Truncate(int64(len(hash))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := hf.WriteAt([]byte(hash), 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hf.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -248,12 +248,26 @@ func (e *invalidImportError) Unwrap() error {
|
||||
// return the module, its root directory, and a list of other modules that
|
||||
// lexically could have provided the package but did not.
|
||||
func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, altMods []module.Version, err error) {
|
||||
invalidf := func(format string, args ...interface{}) (module.Version, string, []module.Version, error) {
|
||||
return module.Version{}, "", nil, &invalidImportError{
|
||||
importPath: path,
|
||||
err: fmt.Errorf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(path, "@") {
|
||||
return module.Version{}, "", nil, fmt.Errorf("import path should not have @version")
|
||||
return invalidf("import path %q should not have @version", path)
|
||||
}
|
||||
if build.IsLocalImport(path) {
|
||||
return module.Version{}, "", nil, fmt.Errorf("relative import not supported")
|
||||
return invalidf("%q is relative, but relative import paths are not supported in module mode", path)
|
||||
}
|
||||
if filepath.IsAbs(path) {
|
||||
return invalidf("%q is not a package path; see 'go help packages'", path)
|
||||
}
|
||||
if search.IsMetaPackage(path) {
|
||||
return invalidf("%q is not an importable package; see 'go help packages'", path)
|
||||
}
|
||||
|
||||
if path == "C" {
|
||||
// There's no directory for import "C".
|
||||
return module.Version{}, "", nil, nil
|
||||
|
@ -288,20 +288,20 @@ func BinDir() string {
|
||||
// operate in workspace mode. It should not be called by other commands,
|
||||
// for example 'go mod tidy', that don't operate in workspace mode.
|
||||
func InitWorkfile() {
|
||||
switch cfg.WorkFile {
|
||||
switch gowork := cfg.Getenv("GOWORK"); gowork {
|
||||
case "off":
|
||||
workFilePath = ""
|
||||
case "", "auto":
|
||||
workFilePath = findWorkspaceFile(base.Cwd())
|
||||
default:
|
||||
if !filepath.IsAbs(cfg.WorkFile) {
|
||||
base.Fatalf("the path provided to -workfile must be an absolute path")
|
||||
if !filepath.IsAbs(gowork) {
|
||||
base.Fatalf("the path provided to GOWORK must be an absolute path")
|
||||
}
|
||||
workFilePath = cfg.WorkFile
|
||||
workFilePath = gowork
|
||||
}
|
||||
}
|
||||
|
||||
// WorkFilePath returns the path of the go.work file, or "" if not in
|
||||
// WorkFilePath returns the absolute path of the go.work file, or "" if not in
|
||||
// workspace mode. WorkFilePath must be called after InitWorkfile.
|
||||
func WorkFilePath() string {
|
||||
return workFilePath
|
||||
@ -610,6 +610,9 @@ func UpdateWorkFile(wf *modfile.WorkFile) {
|
||||
missingModulePaths := map[string]string{} // module directory listed in file -> abspath modroot
|
||||
|
||||
for _, d := range wf.Use {
|
||||
if d.Path == "" {
|
||||
continue // d is marked for deletion.
|
||||
}
|
||||
modRoot := d.Path
|
||||
if d.ModulePath == "" {
|
||||
missingModulePaths[d.Path] = modRoot
|
||||
@ -1030,11 +1033,25 @@ func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile
|
||||
for _, r := range modFiles[i].Replace {
|
||||
if replacedByWorkFile[r.Old.Path] {
|
||||
continue
|
||||
} else if prev, ok := replacements[r.Old]; ok && !curModuleReplaces[r.Old] && prev != r.New {
|
||||
base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v\nuse \"go work edit -replace %v=[override]\" to resolve", r.Old, prev, r.New, r.Old)
|
||||
}
|
||||
var newV module.Version = r.New
|
||||
if WorkFilePath() != "" && newV.Version == "" && !filepath.IsAbs(newV.Path) {
|
||||
// Since we are in a workspace, we may be loading replacements from
|
||||
// multiple go.mod files. Relative paths in those replacement are
|
||||
// relative to the go.mod file, not the workspace, so the same string
|
||||
// may refer to two different paths and different strings may refer to
|
||||
// the same path. Convert them all to be absolute instead.
|
||||
//
|
||||
// (We could do this outside of a workspace too, but it would mean that
|
||||
// replacement paths in error strings needlessly differ from what's in
|
||||
// the go.mod file.)
|
||||
newV.Path = filepath.Join(rootDirs[i], newV.Path)
|
||||
}
|
||||
if prev, ok := replacements[r.Old]; ok && !curModuleReplaces[r.Old] && prev != newV {
|
||||
base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v\nuse \"go work edit -replace %v=[override]\" to resolve", r.Old, prev, newV, r.Old)
|
||||
}
|
||||
curModuleReplaces[r.Old] = true
|
||||
replacements[r.Old] = r.New
|
||||
replacements[r.Old] = newV
|
||||
|
||||
v, ok := mainModules.highestReplaced[r.Old.Path]
|
||||
if !ok || semver.Compare(r.Old.Version, v) > 0 {
|
||||
@ -1092,7 +1109,7 @@ func setDefaultBuildMod() {
|
||||
if inWorkspaceMode() && cfg.BuildMod != "readonly" {
|
||||
base.Fatalf("go: -mod may only be set to readonly when in workspace mode, but it is set to %q"+
|
||||
"\n\tRemove the -mod flag to use the default readonly value,"+
|
||||
"\n\tor set -workfile=off to disable workspace mode.", cfg.BuildMod)
|
||||
"\n\tor set GOWORK=off to disable workspace mode.", cfg.BuildMod)
|
||||
}
|
||||
// Don't override an explicit '-mod=' argument.
|
||||
return
|
||||
|
@ -479,7 +479,11 @@ func matchLocalDirs(ctx context.Context, modRoots []string, m *search.Match, rs
|
||||
}
|
||||
if !found && search.InDir(absDir, cfg.GOROOTsrc) == "" && pathInModuleCache(ctx, absDir, rs) == "" {
|
||||
m.Dirs = []string{}
|
||||
m.AddError(fmt.Errorf("directory prefix %s outside available modules", base.ShortPath(absDir)))
|
||||
scope := "main module or its selected dependencies"
|
||||
if inWorkspaceMode() {
|
||||
scope = "modules listed in go.work or their selected dependencies"
|
||||
}
|
||||
m.AddError(fmt.Errorf("directory prefix %s does not contain %s", base.ShortPath(absDir), scope))
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -601,7 +605,11 @@ func resolveLocalPackage(ctx context.Context, dir string, rs *Requirements) (str
|
||||
|
||||
pkg := pathInModuleCache(ctx, absDir, rs)
|
||||
if pkg == "" {
|
||||
return "", fmt.Errorf("directory %s outside available modules", base.ShortPath(absDir))
|
||||
scope := "main module or its selected dependencies"
|
||||
if inWorkspaceMode() {
|
||||
scope = "modules listed in go.work or their selected dependencies"
|
||||
}
|
||||
return "", fmt.Errorf("directory %s outside %s", base.ShortPath(absDir), scope)
|
||||
}
|
||||
return pkg, nil
|
||||
}
|
||||
@ -1667,24 +1675,6 @@ func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (ch
|
||||
|
||||
// load loads an individual package.
|
||||
func (ld *loader) load(ctx context.Context, pkg *loadPkg) {
|
||||
if strings.Contains(pkg.path, "@") {
|
||||
// Leave for error during load.
|
||||
return
|
||||
}
|
||||
if build.IsLocalImport(pkg.path) || filepath.IsAbs(pkg.path) {
|
||||
// Leave for error during load.
|
||||
// (Module mode does not allow local imports.)
|
||||
return
|
||||
}
|
||||
|
||||
if search.IsMetaPackage(pkg.path) {
|
||||
pkg.err = &invalidImportError{
|
||||
importPath: pkg.path,
|
||||
err: fmt.Errorf("%q is not an importable package; see 'go help packages'", pkg.path),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var mg *ModuleGraph
|
||||
if ld.requirements.pruning == unpruned {
|
||||
var err error
|
||||
|
@ -65,7 +65,6 @@ func init() {
|
||||
CmdRun.Run = runRun // break init loop
|
||||
|
||||
work.AddBuildFlags(CmdRun, work.DefaultBuildFlags)
|
||||
base.AddWorkfileFlag(&CmdRun.Flag)
|
||||
CmdRun.Flag.Var((*base.StringsFlag)(&work.ExecCmd), "exec", "")
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,6 @@ import (
|
||||
|
||||
func init() {
|
||||
work.AddBuildFlags(CmdTest, work.OmitVFlag)
|
||||
base.AddWorkfileFlag(&CmdTest.Flag)
|
||||
|
||||
cf := CmdTest.Flag
|
||||
cf.BoolVar(&testC, "c", false, "")
|
||||
|
@ -6,7 +6,6 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"debug/buildinfo"
|
||||
"errors"
|
||||
@ -156,12 +155,8 @@ func scanFile(file string, info fs.FileInfo, mustPrint bool) {
|
||||
|
||||
fmt.Printf("%s: %s\n", file, bi.GoVersion)
|
||||
bi.GoVersion = "" // suppress printing go version again
|
||||
mod, err := bi.MarshalText()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: formatting build info: %v\n", file, err)
|
||||
return
|
||||
}
|
||||
mod := bi.String()
|
||||
if *versionM && len(mod) > 0 {
|
||||
fmt.Printf("\t%s\n", bytes.ReplaceAll(mod[:len(mod)-1], []byte("\n"), []byte("\n\t")))
|
||||
fmt.Printf("\t%s\n", strings.ReplaceAll(mod[:len(mod)-1], "\n", "\n\t"))
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/trace"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
@ -54,6 +55,7 @@ See also: go fmt, go fix.
|
||||
|
||||
func runVet(ctx context.Context, cmd *base.Command, args []string) {
|
||||
vetFlags, pkgArgs := vetFlags(args)
|
||||
modload.InitWorkfile() // The vet command does custom flag processing; initialize workspaces after that.
|
||||
|
||||
if cfg.DebugTrace != "" {
|
||||
var close func() error
|
||||
|
@ -130,14 +130,6 @@ and test commands:
|
||||
directory, but it is not accessed. When -modfile is specified, an
|
||||
alternate go.sum file is also used: its path is derived from the
|
||||
-modfile flag by trimming the ".mod" extension and appending ".sum".
|
||||
-workfile file
|
||||
in module aware mode, use the given go.work file as a workspace file.
|
||||
By default or when -workfile is "auto", the go command searches for a
|
||||
file named go.work in the current directory and then containing directories
|
||||
until one is found. If a valid go.work file is found, the modules
|
||||
specified will collectively be used as the main modules. If -workfile
|
||||
is "off", or a go.work file is not found in "auto" mode, workspace
|
||||
mode is disabled.
|
||||
-overlay file
|
||||
read a JSON config file that provides an overlay for build operations.
|
||||
The file is a JSON struct with a single field, named 'Replace', that
|
||||
@ -217,7 +209,6 @@ func init() {
|
||||
|
||||
AddBuildFlags(CmdBuild, DefaultBuildFlags)
|
||||
AddBuildFlags(CmdInstall, DefaultBuildFlags)
|
||||
base.AddWorkfileFlag(&CmdBuild.Flag)
|
||||
}
|
||||
|
||||
// Note that flags consulted by other parts of the code
|
||||
|
@ -2015,6 +2015,7 @@ func (b *Builder) showOutput(a *Action, dir, desc, out string) {
|
||||
if reldir := base.ShortPath(dir); reldir != dir {
|
||||
suffix = strings.ReplaceAll(suffix, " "+dir, " "+reldir)
|
||||
suffix = strings.ReplaceAll(suffix, "\n"+dir, "\n"+reldir)
|
||||
suffix = strings.ReplaceAll(suffix, "\n\t"+dir, "\n\t"+reldir)
|
||||
}
|
||||
suffix = strings.ReplaceAll(suffix, " "+b.WorkDir, " $WORK")
|
||||
|
||||
|
@ -131,6 +131,7 @@ var validCompilerFlagsWithNextArg = []string{
|
||||
"-D",
|
||||
"-U",
|
||||
"-I",
|
||||
"-F",
|
||||
"-framework",
|
||||
"-include",
|
||||
"-isysroot",
|
||||
|
@ -15,6 +15,7 @@ var goodCompilerFlags = [][]string{
|
||||
{"-Ufoo"},
|
||||
{"-Ufoo1"},
|
||||
{"-F/Qt"},
|
||||
{"-F", "/Qt"},
|
||||
{"-I/"},
|
||||
{"-I/etc/passwd"},
|
||||
{"-I."},
|
||||
|
@ -63,19 +63,14 @@ writing it back to go.mod.
|
||||
The -json flag prints the final go.work file in JSON format instead of
|
||||
writing it back to go.mod. The JSON output corresponds to these Go types:
|
||||
|
||||
type Module struct {
|
||||
Path string
|
||||
Version string
|
||||
}
|
||||
|
||||
type GoWork struct {
|
||||
Go string
|
||||
Directory []Directory
|
||||
Replace []Replace
|
||||
Go string
|
||||
Use []Use
|
||||
Replace []Replace
|
||||
}
|
||||
|
||||
type Use struct {
|
||||
Path string
|
||||
DiskPath string
|
||||
ModulePath string
|
||||
}
|
||||
|
||||
@ -84,6 +79,11 @@ writing it back to go.mod. The JSON output corresponds to these Go types:
|
||||
New Module
|
||||
}
|
||||
|
||||
type Module struct {
|
||||
Path string
|
||||
Version string
|
||||
}
|
||||
|
||||
See the workspaces design proposal at
|
||||
https://go.googlesource.com/proposal/+/master/design/45713-workspace.md for
|
||||
more information.
|
||||
@ -110,22 +110,9 @@ func init() {
|
||||
cmdEdit.Flag.Var(flagFunc(flagEditworkDropUse), "dropuse", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagEditworkReplace), "replace", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagEditworkDropReplace), "dropreplace", "")
|
||||
|
||||
base.AddWorkfileFlag(&cmdEdit.Flag)
|
||||
}
|
||||
|
||||
func runEditwork(ctx context.Context, cmd *base.Command, args []string) {
|
||||
anyFlags :=
|
||||
*editGo != "" ||
|
||||
*editJSON ||
|
||||
*editPrint ||
|
||||
*editFmt ||
|
||||
len(workedits) > 0
|
||||
|
||||
if !anyFlags {
|
||||
base.Fatalf("go: no flags specified (see 'go help work edit').")
|
||||
}
|
||||
|
||||
if *editJSON && *editPrint {
|
||||
base.Fatalf("go: cannot use both -json and -print")
|
||||
}
|
||||
@ -147,6 +134,21 @@ func runEditwork(ctx context.Context, cmd *base.Command, args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
if gowork == "" {
|
||||
base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using GOWORK environment variable)")
|
||||
}
|
||||
|
||||
anyFlags :=
|
||||
*editGo != "" ||
|
||||
*editJSON ||
|
||||
*editPrint ||
|
||||
*editFmt ||
|
||||
len(workedits) > 0
|
||||
|
||||
if !anyFlags {
|
||||
base.Fatalf("go: no flags specified (see 'go help work edit').")
|
||||
}
|
||||
|
||||
workFile, err := modload.ReadWorkFile(gowork)
|
||||
if err != nil {
|
||||
base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gowork), err)
|
||||
|
@ -33,7 +33,6 @@ current go version will also be listed in the go.work file.
|
||||
|
||||
func init() {
|
||||
base.AddModCommonFlags(&cmdInit.Flag)
|
||||
base.AddWorkfileFlag(&cmdInit.Flag)
|
||||
}
|
||||
|
||||
func runInit(ctx context.Context, cmd *base.Command, args []string) {
|
||||
@ -41,12 +40,10 @@ func runInit(ctx context.Context, cmd *base.Command, args []string) {
|
||||
|
||||
modload.ForceUseModules = true
|
||||
|
||||
// TODO(matloob): support using the -workfile path
|
||||
// To do that properly, we'll have to make the module directories
|
||||
// make dirs relative to workFile path before adding the paths to
|
||||
// the directory entries
|
||||
|
||||
workFile := filepath.Join(base.Cwd(), "go.work")
|
||||
workFile := modload.WorkFilePath()
|
||||
if workFile == "" {
|
||||
workFile = filepath.Join(base.Cwd(), "go.work")
|
||||
}
|
||||
|
||||
modload.CreateWorkFile(ctx, workFile, args)
|
||||
}
|
||||
|
@ -39,13 +39,14 @@ that in each workspace module.
|
||||
|
||||
func init() {
|
||||
base.AddModCommonFlags(&cmdSync.Flag)
|
||||
base.AddWorkfileFlag(&cmdSync.Flag)
|
||||
}
|
||||
|
||||
func runSync(ctx context.Context, cmd *base.Command, args []string) {
|
||||
modload.InitWorkfile()
|
||||
|
||||
modload.ForceUseModules = true
|
||||
modload.InitWorkfile()
|
||||
if modload.WorkFilePath() == "" {
|
||||
base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using GOWORK environment variable)")
|
||||
}
|
||||
|
||||
workGraph := modload.LoadModGraph(ctx, "")
|
||||
_ = workGraph
|
||||
|
@ -10,7 +10,10 @@ import (
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/fsys"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/str"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -39,47 +42,50 @@ func init() {
|
||||
cmdUse.Run = runUse // break init cycle
|
||||
|
||||
base.AddModCommonFlags(&cmdUse.Flag)
|
||||
base.AddWorkfileFlag(&cmdUse.Flag)
|
||||
}
|
||||
|
||||
func runUse(ctx context.Context, cmd *base.Command, args []string) {
|
||||
modload.InitWorkfile()
|
||||
|
||||
modload.ForceUseModules = true
|
||||
|
||||
var gowork string
|
||||
modload.InitWorkfile()
|
||||
gowork = modload.WorkFilePath()
|
||||
|
||||
if gowork == "" {
|
||||
base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using GOWORK environment variable)")
|
||||
}
|
||||
workFile, err := modload.ReadWorkFile(gowork)
|
||||
if err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
workDir := filepath.Dir(gowork) // Absolute, since gowork itself is absolute.
|
||||
|
||||
haveDirs := make(map[string]bool)
|
||||
for _, dir := range workFile.Use {
|
||||
haveDirs[filepath.Join(filepath.Dir(gowork), filepath.FromSlash(dir.Path))] = true
|
||||
haveDirs := make(map[string][]string) // absolute → original(s)
|
||||
for _, use := range workFile.Use {
|
||||
var abs string
|
||||
if filepath.IsAbs(use.Path) {
|
||||
abs = filepath.Clean(use.Path)
|
||||
} else {
|
||||
abs = filepath.Join(workDir, use.Path)
|
||||
}
|
||||
haveDirs[abs] = append(haveDirs[abs], use.Path)
|
||||
}
|
||||
|
||||
addDirs := make(map[string]bool)
|
||||
removeDirs := make(map[string]bool)
|
||||
// keepDirs maps each absolute path to keep to the literal string to use for
|
||||
// that path (either an absolute or a relative path), or the empty string if
|
||||
// all entries for the absolute path should be removed.
|
||||
keepDirs := make(map[string]string)
|
||||
|
||||
// lookDir updates the entry in keepDirs for the directory dir,
|
||||
// which is either absolute or relative to the current working directory
|
||||
// (not necessarily the directory containing the workfile).
|
||||
lookDir := func(dir string) {
|
||||
absDir := filepath.Join(base.Cwd(), dir)
|
||||
// If the path is absolute, keep it absolute. If it's relative,
|
||||
// make it relative to the go.work file rather than the working directory.
|
||||
if !filepath.IsAbs(dir) {
|
||||
rel, err := filepath.Rel(filepath.Dir(gowork), absDir)
|
||||
if err == nil {
|
||||
dir = rel
|
||||
}
|
||||
}
|
||||
fi, err := os.Stat(filepath.Join(dir, "go.mod"))
|
||||
absDir, dir := pathRel(workDir, dir)
|
||||
|
||||
fi, err := os.Stat(filepath.Join(absDir, "go.mod"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
|
||||
if haveDirs[absDir] {
|
||||
removeDirs[dir] = true
|
||||
}
|
||||
keepDirs[absDir] = ""
|
||||
return
|
||||
}
|
||||
base.Errorf("go: %v", err)
|
||||
@ -89,31 +95,96 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) {
|
||||
base.Errorf("go: %v is not regular", filepath.Join(dir, "go.mod"))
|
||||
}
|
||||
|
||||
if !haveDirs[absDir] {
|
||||
addDirs[dir] = true
|
||||
if dup := keepDirs[absDir]; dup != "" && dup != dir {
|
||||
base.Errorf(`go: already added "%s" as "%s"`, dir, dup)
|
||||
}
|
||||
keepDirs[absDir] = dir
|
||||
}
|
||||
|
||||
for _, useDir := range args {
|
||||
if *useR {
|
||||
fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error {
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
lookDir(path)
|
||||
return nil
|
||||
})
|
||||
if !*useR {
|
||||
lookDir(useDir)
|
||||
continue
|
||||
}
|
||||
lookDir(useDir)
|
||||
|
||||
// Add or remove entries for any subdirectories that still exist.
|
||||
err := fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error {
|
||||
if !info.IsDir() {
|
||||
if info.Mode()&fs.ModeSymlink != 0 {
|
||||
if target, err := fsys.Stat(path); err == nil && target.IsDir() {
|
||||
fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
lookDir(path)
|
||||
return nil
|
||||
})
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
base.Errorf("go: %v", err)
|
||||
}
|
||||
|
||||
// Remove entries for subdirectories that no longer exist.
|
||||
// Because they don't exist, they will be skipped by Walk.
|
||||
absArg, _ := pathRel(workDir, useDir)
|
||||
for absDir, _ := range haveDirs {
|
||||
if str.HasFilePathPrefix(absDir, absArg) {
|
||||
if _, ok := keepDirs[absDir]; !ok {
|
||||
keepDirs[absDir] = "" // Mark for deletion.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for dir := range removeDirs {
|
||||
workFile.DropUse(filepath.ToSlash(dir))
|
||||
}
|
||||
for dir := range addDirs {
|
||||
workFile.AddUse(filepath.ToSlash(dir), "")
|
||||
base.ExitIfErrors()
|
||||
|
||||
for absDir, keepDir := range keepDirs {
|
||||
nKept := 0
|
||||
for _, dir := range haveDirs[absDir] {
|
||||
if dir == keepDir { // (note that dir is always non-empty)
|
||||
nKept++
|
||||
} else {
|
||||
workFile.DropUse(dir)
|
||||
}
|
||||
}
|
||||
if keepDir != "" && nKept != 1 {
|
||||
// If we kept more than one copy, delete them all.
|
||||
// We'll recreate a unique copy with AddUse.
|
||||
if nKept > 1 {
|
||||
workFile.DropUse(keepDir)
|
||||
}
|
||||
workFile.AddUse(keepDir, "")
|
||||
}
|
||||
}
|
||||
modload.UpdateWorkFile(workFile)
|
||||
modload.WriteWorkFile(gowork, workFile)
|
||||
}
|
||||
|
||||
// pathRel returns the absolute and canonical forms of dir for use in a
|
||||
// go.work file located in directory workDir.
|
||||
//
|
||||
// If dir is relative, it is intepreted relative to base.Cwd()
|
||||
// and its canonical form is relative to workDir if possible.
|
||||
// If dir is absolute or cannot be made relative to workDir,
|
||||
// its canonical form is absolute.
|
||||
//
|
||||
// Canonical absolute paths are clean.
|
||||
// Canonical relative paths are clean and slash-separated.
|
||||
func pathRel(workDir, dir string) (abs, canonical string) {
|
||||
if filepath.IsAbs(dir) {
|
||||
abs = filepath.Clean(dir)
|
||||
return abs, abs
|
||||
}
|
||||
|
||||
abs = filepath.Join(base.Cwd(), dir)
|
||||
rel, err := filepath.Rel(workDir, abs)
|
||||
if err != nil {
|
||||
// The path can't be made relative to the go.work file,
|
||||
// so it must be kept absolute instead.
|
||||
return abs, abs
|
||||
}
|
||||
|
||||
// Normalize relative paths to use slashes, so that checked-in go.work
|
||||
// files with relative paths within the repo are platform-independent.
|
||||
return abs, filepath.ToSlash(rel)
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ workspace that does not specify modules to be used cannot be used to do
|
||||
builds from local modules.
|
||||
|
||||
go.work files are line-oriented. Each line holds a single directive,
|
||||
made up of a keyword followed by aruments. For example:
|
||||
made up of a keyword followed by arguments. For example:
|
||||
|
||||
go 1.18
|
||||
|
||||
|
@ -142,6 +142,8 @@ var extraEnvKeys = []string{
|
||||
"SYSTEMROOT", // must be preserved on Windows to find DLLs; golang.org/issue/25210
|
||||
"WINDIR", // must be preserved on Windows to be able to run PowerShell command; golang.org/issue/30711
|
||||
"LD_LIBRARY_PATH", // must be preserved on Unix systems to find shared libraries
|
||||
"LIBRARY_PATH", // allow override of non-standard static library paths
|
||||
"C_INCLUDE_PATH", // allow override non-standard include paths
|
||||
"CC", // don't lose user settings when invoking cgo
|
||||
"GO_TESTING_GOTOOLS", // for gccgo testing
|
||||
"GCCGO", // for gccgo testing
|
||||
@ -648,9 +650,9 @@ func (ts *testScript) doCmdCmp(want simpleStatus, args []string, env, quiet bool
|
||||
}
|
||||
case successOrFailure:
|
||||
if eq {
|
||||
fmt.Fprintf(&ts.log, "%s and %s do not differ", name1, name2)
|
||||
fmt.Fprintf(&ts.log, "%s and %s do not differ\n", name1, name2)
|
||||
} else {
|
||||
fmt.Fprintf(&ts.log, "%s and %s differ", name1, name2)
|
||||
fmt.Fprintf(&ts.log, "%s and %s differ\n", name1, name2)
|
||||
}
|
||||
default:
|
||||
ts.fatalf("unsupported: %v cmp", want)
|
||||
@ -902,7 +904,7 @@ func (ts *testScript) cmdStale(want simpleStatus, args []string) {
|
||||
tmpl := "{{if .Error}}{{.ImportPath}}: {{.Error.Err}}{{else}}"
|
||||
switch want {
|
||||
case failure:
|
||||
tmpl += "{{if .Stale}}{{.ImportPath}} is unexpectedly stale{{end}}"
|
||||
tmpl += "{{if .Stale}}{{.ImportPath}} is unexpectedly stale: {{.StaleReason}}{{end}}"
|
||||
case success:
|
||||
tmpl += "{{if not .Stale}}{{.ImportPath}} is unexpectedly NOT stale{{end}}"
|
||||
default:
|
||||
|
@ -10,8 +10,10 @@ stderr 'internal'
|
||||
|
||||
# Test internal packages outside GOROOT are respected
|
||||
cd ../testinternal2
|
||||
env GO111MODULE=off
|
||||
! go build -v .
|
||||
stderr 'p\.go:3:8: use of internal package .*internal/w not allowed'
|
||||
env GO111MODULE=''
|
||||
|
||||
[gccgo] skip # gccgo does not have GOROOT
|
||||
cd ../testinternal
|
||||
|
@ -15,12 +15,13 @@ cp empty $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.partial
|
||||
go mod verify
|
||||
|
||||
# 'go list' should not load packages from the directory.
|
||||
# NOTE: the message "directory $dir outside available modules" is reported
|
||||
# for directories not in the main module, active modules in the module cache,
|
||||
# or local replacements. In this case, the directory is in the right place,
|
||||
# but it's incomplete, so 'go list' acts as if it's not an active module.
|
||||
# NOTE: the message "directory $dir outside main module or its selected dependencies"
|
||||
# is reported for directories not in the main module, active modules in the
|
||||
# module cache, or local replacements. In this case, the directory is in the
|
||||
# right place, but it's incomplete, so 'go list' acts as if it's not an
|
||||
# active module.
|
||||
! go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
|
||||
stderr 'outside available modules'
|
||||
stderr 'outside main module or its selected dependencies'
|
||||
|
||||
# 'go list -m' should not print the directory.
|
||||
go list -m -f '{{.Dir}}' rsc.io/quote
|
||||
|
@ -51,11 +51,11 @@ stdout '^at$'
|
||||
# a package path.
|
||||
cd ../badat/bad@
|
||||
! go list .
|
||||
stderr 'directory . outside available modules'
|
||||
stderr 'directory . outside main module or its selected dependencies'
|
||||
! go list $PWD
|
||||
stderr 'directory . outside available modules'
|
||||
stderr 'directory . outside main module or its selected dependencies'
|
||||
! go list $PWD/...
|
||||
stderr 'directory . outside available modules'
|
||||
stderr 'directory . outside main module or its selected dependencies'
|
||||
|
||||
-- x/go.mod --
|
||||
module m
|
||||
|
@ -194,10 +194,10 @@ cp go.mod.orig go.mod
|
||||
go mod edit -require github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d+incompatible
|
||||
cd outside
|
||||
! go list -m github.com/pierrec/lz4
|
||||
stderr 'go: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
|
||||
stderr '^go: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
|
||||
cd ..
|
||||
! go list -m github.com/pierrec/lz4
|
||||
stderr 'github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
|
||||
stderr '^go: github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
|
||||
|
||||
# A +incompatible pseudo-version is valid for a revision of the module
|
||||
# that lacks a go.mod file.
|
||||
@ -222,7 +222,7 @@ stdout 'github.com/pierrec/lz4 v2.0.5\+incompatible'
|
||||
# not resolve to a pseudo-version with a different major version.
|
||||
cp go.mod.orig go.mod
|
||||
! go get github.com/pierrec/lz4@v2.0.8
|
||||
stderr 'go: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2'
|
||||
stderr 'go: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
|
||||
|
||||
# An invalid +incompatible suffix for a canonical version should error out,
|
||||
# not resolve to a pseudo-version.
|
||||
@ -233,10 +233,10 @@ cp go.mod.orig go.mod
|
||||
go mod edit -require github.com/pierrec/lz4@v2.0.8+incompatible
|
||||
cd outside
|
||||
! go list -m github.com/pierrec/lz4
|
||||
stderr 'github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
|
||||
stderr '^go: github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
|
||||
cd ..
|
||||
! go list -m github.com/pierrec/lz4
|
||||
stderr 'github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
|
||||
stderr '^go: github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
|
||||
|
||||
-- go.mod.orig --
|
||||
module example.com
|
||||
|
@ -24,7 +24,7 @@ go get rsc.io/sampler@v1.3.1
|
||||
go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.1
|
||||
stdout '^rsc.io/sampler$'
|
||||
! go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.0
|
||||
stderr 'outside available modules'
|
||||
stderr 'outside main module or its selected dependencies'
|
||||
|
||||
-- go.mod --
|
||||
module x
|
||||
|
@ -9,7 +9,7 @@ go get
|
||||
go mod download rsc.io/quote@v1.5.2
|
||||
|
||||
! go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
|
||||
stderr '^directory ..[/\\]pkg[/\\]mod[/\\]rsc.io[/\\]quote@v1.5.2 outside available modules$'
|
||||
stderr '^directory ..[/\\]pkg[/\\]mod[/\\]rsc.io[/\\]quote@v1.5.2 outside main module or its selected dependencies$'
|
||||
|
||||
go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.1
|
||||
stdout 'rsc.io/quote'
|
||||
|
54
libgo/go/cmd/go/testdata/script/run_issue51125.txt
vendored
Normal file
54
libgo/go/cmd/go/testdata/script/run_issue51125.txt
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
# Regression test for https://go.dev/issue/51125:
|
||||
# Relative import paths (a holdover from GOPATH) were accidentally allowed in module mode.
|
||||
|
||||
cd $WORK
|
||||
|
||||
# Relative imports should not be allowed with a go.mod file.
|
||||
|
||||
! go run driver.go
|
||||
stderr '^driver.go:3:8: "./mypkg" is relative, but relative import paths are not supported in module mode$'
|
||||
|
||||
go list -e -f '{{with .Error}}{{.}}{{end}}' -deps driver.go
|
||||
stdout '^driver.go:3:8: "./mypkg" is relative, but relative import paths are not supported in module mode$'
|
||||
! stderr .
|
||||
|
||||
|
||||
# Relative imports should not be allowed in module mode even without a go.mod file.
|
||||
rm go.mod
|
||||
|
||||
! go run driver.go
|
||||
stderr '^driver.go:3:8: "./mypkg" is relative, but relative import paths are not supported in module mode$'
|
||||
|
||||
go list -e -f '{{with .Error}}{{.}}{{end}}' -deps driver.go
|
||||
stdout '^driver.go:3:8: "./mypkg" is relative, but relative import paths are not supported in module mode$'
|
||||
! stderr .
|
||||
|
||||
|
||||
# In GOPATH mode, they're still allowed (but only outside of GOPATH/src).
|
||||
env GO111MODULE=off
|
||||
|
||||
[!short] go run driver.go
|
||||
|
||||
go list -deps driver.go
|
||||
|
||||
|
||||
-- $WORK/go.mod --
|
||||
module example
|
||||
|
||||
go 1.17
|
||||
-- $WORK/driver.go --
|
||||
package main
|
||||
|
||||
import "./mypkg"
|
||||
|
||||
func main() {
|
||||
mypkg.MyFunc()
|
||||
}
|
||||
-- $WORK/mypkg/code.go --
|
||||
package mypkg
|
||||
|
||||
import "fmt"
|
||||
|
||||
func MyFunc() {
|
||||
fmt.Println("Hello, world!")
|
||||
}
|
52
libgo/go/cmd/go/testdata/script/test_fuzz_dup_cache.txt
vendored
Normal file
52
libgo/go/cmd/go/testdata/script/test_fuzz_dup_cache.txt
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
[!fuzz] skip
|
||||
[short] skip
|
||||
|
||||
# This test checks that cached corpus loading properly handles duplicate entries (this can
|
||||
# happen when a f.Add value has a duplicate entry in the cached corpus.) Duplicate entries
|
||||
# should be discarded, and the rest of the cache should be loaded as normal.
|
||||
|
||||
env GOCACHE=$WORK/cache
|
||||
env GODEBUG=fuzzdebug=1
|
||||
|
||||
mkdir -p $GOCACHE/fuzz/fuzztest/FuzzTarget
|
||||
go run ./populate $GOCACHE/fuzz/fuzztest/FuzzTarget
|
||||
|
||||
go test -fuzz=FuzzTarget -fuzztime=10x .
|
||||
stdout 'entries: 5'
|
||||
|
||||
-- go.mod --
|
||||
module fuzztest
|
||||
|
||||
go 1.17
|
||||
|
||||
-- fuzz_test.go --
|
||||
package fuzz
|
||||
|
||||
import "testing"
|
||||
|
||||
func FuzzTarget(f *testing.F) {
|
||||
f.Add(int(0))
|
||||
f.Fuzz(func(t *testing.T, _ int) {})
|
||||
}
|
||||
|
||||
-- populate/main.go --
|
||||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 10; i++ {
|
||||
b := byte(0)
|
||||
if i > 5 {
|
||||
b = byte(i)
|
||||
}
|
||||
tmpl := "go test fuzz v1\nint(%d)\n"
|
||||
if err := os.WriteFile(filepath.Join(os.Args[1], fmt.Sprint(i)), []byte(fmt.Sprintf(tmpl, b)), 0777); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
19
libgo/go/cmd/go/testdata/script/test_fuzz_return.txt
vendored
Normal file
19
libgo/go/cmd/go/testdata/script/test_fuzz_return.txt
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
[short] skip
|
||||
|
||||
! go test .
|
||||
stdout '^panic: testing: fuzz target must not return a value \[recovered\]$'
|
||||
|
||||
-- go.mod --
|
||||
module test
|
||||
go 1.18
|
||||
-- x_test.go --
|
||||
package test
|
||||
|
||||
import "testing"
|
||||
|
||||
func FuzzReturnErr(f *testing.F) {
|
||||
f.Add("hello, validation!")
|
||||
f.Fuzz(func(t *testing.T, in string) string {
|
||||
return in
|
||||
})
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
# Relative imports in command line package
|
||||
|
||||
env GO111MODULE=off
|
||||
|
||||
# Run tests outside GOPATH.
|
||||
env GO111MODULE=off
|
||||
env GOPATH=$WORK/tmp
|
||||
|
8
libgo/go/cmd/go/testdata/script/work.txt
vendored
8
libgo/go/cmd/go/testdata/script/work.txt
vendored
@ -32,7 +32,9 @@ stdout 'example.com/b'
|
||||
go list -mod=readonly all
|
||||
! go list -mod=mod all
|
||||
stderr '^go: -mod may only be set to readonly when in workspace mode'
|
||||
go list -mod=mod -workfile=off all
|
||||
env GOWORK=off
|
||||
go list -mod=mod all
|
||||
env GOWORK=
|
||||
|
||||
# Test that duplicates in the use list return an error
|
||||
cp go.work go.work.backup
|
||||
@ -53,7 +55,9 @@ go run example.com/d
|
||||
# This exercises the code that determines which module command-line-arguments
|
||||
# belongs to.
|
||||
go list ./b/main.go
|
||||
go build -n -workfile=off -o foo foo.go
|
||||
env GOWORK=off
|
||||
go build -n -o foo foo.go
|
||||
env GOWORK=
|
||||
go build -n -o foo foo.go
|
||||
|
||||
-- go.work.dup --
|
||||
|
@ -30,7 +30,8 @@ cmp stdout go.work.want_print
|
||||
go work edit -json -go 1.19 -use b -dropuse c -replace 'x.1@v1.4.0 = ../z' -dropreplace x.1 -dropreplace x.1@v1.3.0
|
||||
cmp stdout go.work.want_json
|
||||
|
||||
go work edit -print -fmt -workfile $GOPATH/src/unformatted
|
||||
env GOWORK=$GOPATH/src/unformatted
|
||||
go work edit -print -fmt
|
||||
cmp stdout formatted
|
||||
|
||||
-- m/go.mod --
|
||||
|
4
libgo/go/cmd/go/testdata/script/work_env.txt
vendored
4
libgo/go/cmd/go/testdata/script/work_env.txt
vendored
@ -13,6 +13,10 @@ cd src
|
||||
go env GOWORK
|
||||
stdout 'go.work'
|
||||
|
||||
env GOWORK='off'
|
||||
go env GOWORK
|
||||
stdout 'off'
|
||||
|
||||
! go env -w GOWORK=off
|
||||
stderr '^go: GOWORK cannot be modified$'
|
||||
|
||||
|
24
libgo/go/cmd/go/testdata/script/work_gowork.txt
vendored
Normal file
24
libgo/go/cmd/go/testdata/script/work_gowork.txt
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
env GOWORK=stop.work
|
||||
! go list a # require absolute path
|
||||
! stderr panic
|
||||
env GOWORK=doesnotexist
|
||||
! go list a
|
||||
! stderr panic
|
||||
|
||||
env GOWORK=$GOPATH/src/stop.work
|
||||
go list -n a
|
||||
go build -n a
|
||||
go test -n a
|
||||
|
||||
-- stop.work --
|
||||
go 1.18
|
||||
|
||||
use ./a
|
||||
-- a/a.go --
|
||||
package a
|
||||
-- a/a_test.go --
|
||||
package a
|
||||
-- a/go.mod --
|
||||
module a
|
||||
|
||||
go 1.18
|
19
libgo/go/cmd/go/testdata/script/work_init_gowork.txt
vendored
Normal file
19
libgo/go/cmd/go/testdata/script/work_init_gowork.txt
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
# Test that the GOWORK environment variable flag is used by go work init.
|
||||
|
||||
! exists go.work
|
||||
go work init
|
||||
exists go.work
|
||||
|
||||
env GOWORK=$GOPATH/src/foo/foo.work
|
||||
! exists foo/foo.work
|
||||
go work init
|
||||
exists foo/foo.work
|
||||
|
||||
env GOWORK=
|
||||
cd foo/bar
|
||||
! go work init
|
||||
stderr 'already exists'
|
||||
|
||||
# Create directories to make go.work files in.
|
||||
-- foo/dummy.txt --
|
||||
-- foo/bar/dummy.txt --
|
57
libgo/go/cmd/go/testdata/script/work_issue51204.txt
vendored
Normal file
57
libgo/go/cmd/go/testdata/script/work_issue51204.txt
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
go work sync
|
||||
|
||||
go list -f '{{.Dir}}' example.com/test
|
||||
stdout '^'$PWD${/}test'$'
|
||||
|
||||
-- go.work --
|
||||
go 1.18
|
||||
|
||||
use (
|
||||
./test2
|
||||
./test2/sub
|
||||
)
|
||||
-- test/go.mod --
|
||||
module example.com/test
|
||||
|
||||
go 1.18
|
||||
-- test/file.go --
|
||||
package test
|
||||
|
||||
func DoSomething() {
|
||||
}
|
||||
-- test2/go.mod --
|
||||
module example.com/test2
|
||||
|
||||
go 1.18
|
||||
|
||||
replace example.com/test => ../test
|
||||
|
||||
require example.com/test v0.0.0-00010101000000-000000000000
|
||||
-- test2/file.go --
|
||||
package test2
|
||||
|
||||
import (
|
||||
"example.com/test"
|
||||
)
|
||||
|
||||
func DoSomething() {
|
||||
test.DoSomething()
|
||||
}
|
||||
-- test2/sub/go.mod --
|
||||
module example.com/test2/sub
|
||||
|
||||
go 1.18
|
||||
|
||||
replace example.com/test => ../../test
|
||||
|
||||
require example.com/test v0.0.0
|
||||
-- test2/sub/file.go --
|
||||
package test2
|
||||
|
||||
import (
|
||||
"example.com/test"
|
||||
)
|
||||
|
||||
func DoSomething() {
|
||||
test.DoSomething()
|
||||
}
|
25
libgo/go/cmd/go/testdata/script/work_module_not_in_go_work.txt
vendored
Normal file
25
libgo/go/cmd/go/testdata/script/work_module_not_in_go_work.txt
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# This is a regression test for issue #49632.
|
||||
# The Go command should mention go.work if the user
|
||||
# tries to load a local package that's in a module
|
||||
# that's not in go.work and can't be resolved.
|
||||
|
||||
! go list ./...
|
||||
stderr 'pattern ./...: directory prefix . does not contain modules listed in go.work or their selected dependencies'
|
||||
|
||||
! go list ./a
|
||||
stderr 'directory a outside modules listed in go.work'
|
||||
|
||||
-- go.work --
|
||||
go 1.18
|
||||
|
||||
use ./b
|
||||
-- a/go.mod --
|
||||
module example.com/a
|
||||
|
||||
go 1.18
|
||||
-- a/a.go --
|
||||
package a
|
||||
-- b/go.mod --
|
||||
module example.com/b
|
||||
|
||||
go 1.18
|
20
libgo/go/cmd/go/testdata/script/work_nowork.txt
vendored
Normal file
20
libgo/go/cmd/go/testdata/script/work_nowork.txt
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
! go work use
|
||||
stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using GOWORK environment variable\)$'
|
||||
|
||||
! go work use .
|
||||
stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using GOWORK environment variable\)$'
|
||||
|
||||
! go work edit
|
||||
stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using GOWORK environment variable\)$'
|
||||
|
||||
! go work edit -go=1.18
|
||||
stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using GOWORK environment variable\)$'
|
||||
|
||||
! go work sync
|
||||
stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using GOWORK environment variable\)$'
|
||||
|
||||
-- go.mod --
|
||||
module example
|
||||
go 1.18
|
||||
-- README.txt --
|
||||
There is no go.work file here.
|
@ -2,7 +2,7 @@
|
||||
# overriding it in the go.work file.
|
||||
|
||||
! go list -m example.com/dep
|
||||
stderr 'go: conflicting replacements for example.com/dep@v1.0.0:\n\t./dep1\n\t./dep2\nuse "go work edit -replace example.com/dep@v1.0.0=\[override\]" to resolve'
|
||||
stderr 'go: conflicting replacements for example.com/dep@v1.0.0:\n\t'$PWD${/}'dep1\n\t'$PWD${/}'dep2\nuse "go work edit -replace example.com/dep@v1.0.0=\[override\]" to resolve'
|
||||
go work edit -replace example.com/dep@v1.0.0=./dep1
|
||||
go list -m example.com/dep
|
||||
stdout 'example.com/dep v1.0.0 => ./dep1'
|
||||
@ -15,7 +15,7 @@ use n
|
||||
module example.com/m
|
||||
|
||||
require example.com/dep v1.0.0
|
||||
replace example.com/dep v1.0.0 => ./dep1
|
||||
replace example.com/dep v1.0.0 => ../dep1
|
||||
-- m/m.go --
|
||||
package m
|
||||
|
||||
@ -28,7 +28,7 @@ func F() {
|
||||
module example.com/n
|
||||
|
||||
require example.com/dep v1.0.0
|
||||
replace example.com/dep v1.0.0 => ./dep2
|
||||
replace example.com/dep v1.0.0 => ../dep2
|
||||
-- n/n.go --
|
||||
package n
|
||||
|
||||
|
22
libgo/go/cmd/go/testdata/script/work_use_deleted.txt
vendored
Normal file
22
libgo/go/cmd/go/testdata/script/work_use_deleted.txt
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
go work use -r .
|
||||
cmp go.work go.work.want
|
||||
|
||||
-- go.work --
|
||||
go 1.18
|
||||
|
||||
use (
|
||||
.
|
||||
sub
|
||||
sub/dir/deleted
|
||||
)
|
||||
-- go.work.want --
|
||||
go 1.18
|
||||
|
||||
use sub/dir
|
||||
-- sub/README.txt --
|
||||
A go.mod file has been deleted from this directory.
|
||||
In addition, the entire subdirectory sub/dir/deleted
|
||||
has been deleted, along with sub/dir/deleted/go.mod.
|
||||
-- sub/dir/go.mod --
|
||||
module example/sub/dir
|
||||
go 1.18
|
49
libgo/go/cmd/go/testdata/script/work_use_dot.txt
vendored
Normal file
49
libgo/go/cmd/go/testdata/script/work_use_dot.txt
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
cp go.work go.work.orig
|
||||
|
||||
# If the current directory contains a go.mod file,
|
||||
# 'go work use .' should add an entry for it.
|
||||
cd bar/baz
|
||||
go work use .
|
||||
cmp ../../go.work ../../go.work.rel
|
||||
|
||||
# If the current directory lacks a go.mod file, 'go work use .'
|
||||
# should remove its entry.
|
||||
mv go.mod go.mod.bak
|
||||
go work use .
|
||||
cmp ../../go.work ../../go.work.orig
|
||||
|
||||
# If the path is absolute, it should remain absolute.
|
||||
mv go.mod.bak go.mod
|
||||
go work use $PWD
|
||||
grep -count=1 '^use ' ../../go.work
|
||||
grep '^use ["]?'$PWD'["]?$' ../../go.work
|
||||
|
||||
# An absolute path should replace an entry for the corresponding relative path
|
||||
# and vice-versa.
|
||||
go work use .
|
||||
cmp ../../go.work ../../go.work.rel
|
||||
go work use $PWD
|
||||
grep -count=1 '^use ' ../../go.work
|
||||
grep '^use ["]?'$PWD'["]?$' ../../go.work
|
||||
|
||||
# If both the absolute and relative paths are named, 'go work use' should error
|
||||
# out: we don't know which one to use, and shouldn't add both because the
|
||||
# resulting workspace would contain a duplicate module.
|
||||
cp ../../go.work.orig ../../go.work
|
||||
! go work use $PWD .
|
||||
stderr '^go: already added "bar/baz" as "'$PWD'"$'
|
||||
cmp ../../go.work ../../go.work.orig
|
||||
|
||||
|
||||
-- go.mod --
|
||||
module example
|
||||
go 1.18
|
||||
-- go.work --
|
||||
go 1.18
|
||||
-- go.work.rel --
|
||||
go 1.18
|
||||
|
||||
use bar/baz
|
||||
-- bar/baz/go.mod --
|
||||
module example/bar/baz
|
||||
go 1.18
|
17
libgo/go/cmd/go/testdata/script/work_use_issue50958.txt
vendored
Normal file
17
libgo/go/cmd/go/testdata/script/work_use_issue50958.txt
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
go work use -r .
|
||||
cmp go.work go.work.want
|
||||
|
||||
-- go.mod --
|
||||
module example
|
||||
go 1.18
|
||||
-- go.work --
|
||||
go 1.18
|
||||
|
||||
use sub
|
||||
-- go.work.want --
|
||||
go 1.18
|
||||
|
||||
use .
|
||||
-- sub/README.txt --
|
||||
This directory no longer contains a go.mod file.
|
||||
|
19
libgo/go/cmd/go/testdata/script/work_vet.txt
vendored
Normal file
19
libgo/go/cmd/go/testdata/script/work_vet.txt
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
! go vet ./a
|
||||
stderr 'fmt.Println call has possible formatting directive'
|
||||
|
||||
-- go.work --
|
||||
go 1.18
|
||||
|
||||
use ./a
|
||||
-- a/go.mod --
|
||||
module example.com/a
|
||||
|
||||
go 1.18
|
||||
-- a/a.go --
|
||||
package a
|
||||
|
||||
import "fmt"
|
||||
|
||||
func A() {
|
||||
fmt.Println("%s")
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
! go list -workfile=stop.work a # require absolute path
|
||||
! stderr panic
|
||||
! go list -workfile=doesnotexist a
|
||||
! stderr panic
|
||||
|
||||
go list -n -workfile=$GOPATH/src/stop.work a
|
||||
go build -n -workfile=$GOPATH/src/stop.work a
|
||||
go test -n -workfile=$GOPATH/src/stop.work a
|
||||
|
||||
-- stop.work --
|
||||
go 1.18
|
||||
|
||||
use ./a
|
||||
-- a/a.go --
|
||||
package a
|
||||
-- a/a_test.go --
|
||||
package a
|
||||
-- a/go.mod --
|
||||
module a
|
||||
|
||||
go 1.18
|
@ -52,6 +52,16 @@ const (
|
||||
printerNormalizeNumbers = 1 << 30
|
||||
)
|
||||
|
||||
// fdSem guards the number of concurrently-open file descriptors.
|
||||
//
|
||||
// For now, this is arbitrarily set to 200, based on the observation that many
|
||||
// platforms default to a kernel limit of 256. Ideally, perhaps we should derive
|
||||
// it from rlimit on platforms that support that system call.
|
||||
//
|
||||
// File descriptors opened from outside of this package are not tracked,
|
||||
// so this limit may be approximate.
|
||||
var fdSem = make(chan bool, 200)
|
||||
|
||||
var (
|
||||
rewrite func(*token.FileSet, *ast.File) *ast.File
|
||||
parserMode parser.Mode
|
||||
@ -213,51 +223,9 @@ func (r *reporter) ExitCode() int {
|
||||
// If info == nil, we are formatting stdin instead of a file.
|
||||
// If in == nil, the source is the contents of the file with the given filename.
|
||||
func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter) error {
|
||||
if in == nil {
|
||||
var err error
|
||||
in, err = os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the file's size and read its contents with minimal allocations.
|
||||
//
|
||||
// If the size is unknown (or bogus, or overflows an int), fall back to
|
||||
// a size-independent ReadAll.
|
||||
var src []byte
|
||||
size := -1
|
||||
if info != nil && info.Mode().IsRegular() && int64(int(info.Size())) == info.Size() {
|
||||
size = int(info.Size())
|
||||
}
|
||||
if size+1 > 0 {
|
||||
// If we have the FileInfo from filepath.WalkDir, use it to make
|
||||
// a buffer of the right size and avoid ReadAll's reallocations.
|
||||
//
|
||||
// We try to read size+1 bytes so that we can detect modifications: if we
|
||||
// read more than size bytes, then the file was modified concurrently.
|
||||
// (If that happens, we could, say, append to src to finish the read, or
|
||||
// proceed with a truncated buffer — but the fact that it changed at all
|
||||
// indicates a possible race with someone editing the file, so we prefer to
|
||||
// stop to avoid corrupting it.)
|
||||
src = make([]byte, size+1)
|
||||
n, err := io.ReadFull(in, src)
|
||||
if err != nil && err != io.ErrUnexpectedEOF {
|
||||
return err
|
||||
}
|
||||
if n < size {
|
||||
return fmt.Errorf("error: size of %s changed during reading (from %d to %d bytes)", filename, size, n)
|
||||
} else if n > size {
|
||||
return fmt.Errorf("error: size of %s changed during reading (from %d to >=%d bytes)", filename, size, len(src))
|
||||
}
|
||||
src = src[:n]
|
||||
} else {
|
||||
// The file is not known to be regular, so we don't have a reliable size for it.
|
||||
var err error
|
||||
src, err = io.ReadAll(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
src, err := readFile(filename, info, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileSet := token.NewFileSet()
|
||||
@ -306,7 +274,9 @@ func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter) e
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fdSem <- true
|
||||
err = os.WriteFile(filename, res, perm)
|
||||
<-fdSem
|
||||
if err != nil {
|
||||
os.Rename(bakname, filename)
|
||||
return err
|
||||
@ -333,6 +303,65 @@ func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter) e
|
||||
return err
|
||||
}
|
||||
|
||||
// readFile reads the contents of filename, described by info.
|
||||
// If in is non-nil, readFile reads directly from it.
|
||||
// Otherwise, readFile opens and reads the file itself,
|
||||
// with the number of concurrently-open files limited by fdSem.
|
||||
func readFile(filename string, info fs.FileInfo, in io.Reader) ([]byte, error) {
|
||||
if in == nil {
|
||||
fdSem <- true
|
||||
var err error
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in = f
|
||||
defer func() {
|
||||
f.Close()
|
||||
<-fdSem
|
||||
}()
|
||||
}
|
||||
|
||||
// Compute the file's size and read its contents with minimal allocations.
|
||||
//
|
||||
// If we have the FileInfo from filepath.WalkDir, use it to make
|
||||
// a buffer of the right size and avoid ReadAll's reallocations.
|
||||
//
|
||||
// If the size is unknown (or bogus, or overflows an int), fall back to
|
||||
// a size-independent ReadAll.
|
||||
size := -1
|
||||
if info != nil && info.Mode().IsRegular() && int64(int(info.Size())) == info.Size() {
|
||||
size = int(info.Size())
|
||||
}
|
||||
if size+1 <= 0 {
|
||||
// The file is not known to be regular, so we don't have a reliable size for it.
|
||||
var err error
|
||||
src, err := io.ReadAll(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
|
||||
// We try to read size+1 bytes so that we can detect modifications: if we
|
||||
// read more than size bytes, then the file was modified concurrently.
|
||||
// (If that happens, we could, say, append to src to finish the read, or
|
||||
// proceed with a truncated buffer — but the fact that it changed at all
|
||||
// indicates a possible race with someone editing the file, so we prefer to
|
||||
// stop to avoid corrupting it.)
|
||||
src := make([]byte, size+1)
|
||||
n, err := io.ReadFull(in, src)
|
||||
if err != nil && err != io.ErrUnexpectedEOF {
|
||||
return nil, err
|
||||
}
|
||||
if n < size {
|
||||
return nil, fmt.Errorf("error: size of %s changed during reading (from %d to %d bytes)", filename, size, n)
|
||||
} else if n > size {
|
||||
return nil, fmt.Errorf("error: size of %s changed during reading (from %d to >=%d bytes)", filename, size, len(src))
|
||||
}
|
||||
return src[:n], nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Arbitrarily limit in-flight work to 2MiB times the number of threads.
|
||||
//
|
||||
@ -354,12 +383,16 @@ func gofmtMain(s *sequencer) {
|
||||
flag.Parse()
|
||||
|
||||
if *cpuprofile != "" {
|
||||
fdSem <- true
|
||||
f, err := os.Create(*cpuprofile)
|
||||
if err != nil {
|
||||
s.AddReport(fmt.Errorf("creating cpu profile: %s", err))
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
defer func() {
|
||||
f.Close()
|
||||
<-fdSem
|
||||
}()
|
||||
pprof.StartCPUProfile(f)
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
@ -474,6 +507,9 @@ const chmodSupported = runtime.GOOS != "windows"
|
||||
// with <number randomly chosen such that the file name is unique. backupFile returns
|
||||
// the chosen file name.
|
||||
func backupFile(filename string, data []byte, perm fs.FileMode) (string, error) {
|
||||
fdSem <- true
|
||||
defer func() { <-fdSem }()
|
||||
|
||||
// create backup file
|
||||
f, err := os.CreateTemp(filepath.Dir(filename), filepath.Base(filename))
|
||||
if err != nil {
|
||||
|
@ -23,6 +23,7 @@ const (
|
||||
FUNCDATA_OpenCodedDeferInfo = 4
|
||||
FUNCDATA_ArgInfo = 5
|
||||
FUNCDATA_ArgLiveInfo = 6
|
||||
FUNCDATA_WrapInfo = 7
|
||||
|
||||
// ArgsSizeUnknown is set in Func.argsize to mark all functions
|
||||
// whose argument size is unknown (C vararg functions, and
|
||||
|
@ -3,28 +3,21 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ecdsa implements the Elliptic Curve Digital Signature Algorithm, as
|
||||
// defined in FIPS 186-3.
|
||||
// defined in FIPS 186-4 and SEC 1, Version 2.0.
|
||||
//
|
||||
// This implementation derives the nonce from an AES-CTR CSPRNG keyed by:
|
||||
//
|
||||
// SHA2-512(priv.D || entropy || hash)[:32]
|
||||
//
|
||||
// The CSPRNG key is indifferentiable from a random oracle as shown in
|
||||
// [Coron], the AES-CTR stream is indifferentiable from a random oracle
|
||||
// under standard cryptographic assumptions (see [Larsson] for examples).
|
||||
//
|
||||
// References:
|
||||
// [Coron]
|
||||
// https://cs.nyu.edu/~dodis/ps/merkle.pdf
|
||||
// [Larsson]
|
||||
// https://web.archive.org/web/20040719170906/https://www.nada.kth.se/kurser/kth/2D1441/semteo03/lecturenotes/assump.pdf
|
||||
// Signatures generated by this package are not deterministic, but entropy is
|
||||
// mixed with the private key and the message, achieving the same level of
|
||||
// security in case of randomness source failure.
|
||||
package ecdsa
|
||||
|
||||
// Further references:
|
||||
// [NSA]: Suite B implementer's guide to FIPS 186-3
|
||||
// https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/suite-b-implementers-guide-to-fips-186-3-ecdsa.cfm
|
||||
// [SECG]: SECG, SEC1
|
||||
// http://www.secg.org/sec1-v2.pdf
|
||||
// [FIPS 186-4] references ANSI X9.62-2005 for the bulk of the ECDSA algorithm.
|
||||
// That standard is not freely available, which is a problem in an open source
|
||||
// implementation, because not only the implementer, but also any maintainer,
|
||||
// contributor, reviewer, auditor, and learner needs access to it. Instead, this
|
||||
// package references and follows the equivalent [SEC 1, Version 2.0].
|
||||
//
|
||||
// [FIPS 186-4]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
|
||||
// [SEC 1, Version 2.0]: https://www.secg.org/sec1-v2.pdf
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
@ -41,15 +34,16 @@ import (
|
||||
"golang.org/x/crypto/cryptobyte/asn1"
|
||||
)
|
||||
|
||||
// A invertible implements fast inverse mod Curve.Params().N
|
||||
// A invertible implements fast inverse in GF(N).
|
||||
type invertible interface {
|
||||
// Inverse returns the inverse of k in GF(P)
|
||||
// Inverse returns the inverse of k mod Params().N.
|
||||
Inverse(k *big.Int) *big.Int
|
||||
}
|
||||
|
||||
// combinedMult implements fast multiplication S1*g + S2*p (g - generator, p - arbitrary point)
|
||||
// A combinedMult implements fast combined multiplication for verification.
|
||||
type combinedMult interface {
|
||||
CombinedMult(bigX, bigY *big.Int, baseScalar, scalar []byte) (x, y *big.Int)
|
||||
// CombinedMult returns [s1]G + [s2]P where G is the generator.
|
||||
CombinedMult(Px, Py *big.Int, s1, s2 []byte) (x, y *big.Int)
|
||||
}
|
||||
|
||||
const (
|
||||
@ -111,7 +105,7 @@ func (priv *PrivateKey) Equal(x crypto.PrivateKey) bool {
|
||||
//
|
||||
// This method implements crypto.Signer, which is an interface to support keys
|
||||
// where the private part is kept in, for example, a hardware module. Common
|
||||
// uses should use the Sign function in this package directly.
|
||||
// uses can use the SignASN1 function in this package directly.
|
||||
func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||
r, s, err := Sign(rand, priv, digest)
|
||||
if err != nil {
|
||||
@ -128,11 +122,13 @@ func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOp
|
||||
|
||||
var one = new(big.Int).SetInt64(1)
|
||||
|
||||
// randFieldElement returns a random element of the field underlying the given
|
||||
// curve using the procedure given in [NSA] A.2.1.
|
||||
// randFieldElement returns a random element of the order of the given
|
||||
// curve using the procedure given in FIPS 186-4, Appendix B.5.1.
|
||||
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
|
||||
params := c.Params()
|
||||
b := make([]byte, params.BitSize/8+8)
|
||||
// Note that for P-521 this will actually be 63 bits more than the order, as
|
||||
// division rounds down, but the extra bit is inconsequential.
|
||||
b := make([]byte, params.BitSize/8+8) // TODO: use params.N.BitLen()
|
||||
_, err = io.ReadFull(rand, b)
|
||||
if err != nil {
|
||||
return
|
||||
@ -159,12 +155,9 @@ func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) {
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
// hashToInt converts a hash value to an integer. There is some disagreement
|
||||
// about how this is done. [NSA] suggests that this is done in the obvious
|
||||
// manner, but [SECG] truncates the hash to the bit-length of the curve order
|
||||
// first. We follow [SECG] because that's what OpenSSL does. Additionally,
|
||||
// OpenSSL right shifts excess bits from the number if the hash is too large
|
||||
// and we mirror that too.
|
||||
// hashToInt converts a hash value to an integer. Per FIPS 186-4, Section 6.4,
|
||||
// we use the left-most bits of the hash to match the bit-length of the order of
|
||||
// the curve. This also performs Step 5 of SEC 1, Version 2.0, Section 4.1.3.
|
||||
func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
|
||||
orderBits := c.Params().N.BitLen()
|
||||
orderBytes := (orderBits + 7) / 8
|
||||
@ -180,10 +173,11 @@ func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
|
||||
return ret
|
||||
}
|
||||
|
||||
// fermatInverse calculates the inverse of k in GF(P) using Fermat's method.
|
||||
// This has better constant-time properties than Euclid's method (implemented
|
||||
// in math/big.Int.ModInverse) although math/big itself isn't strictly
|
||||
// constant-time so it's not perfect.
|
||||
// fermatInverse calculates the inverse of k in GF(P) using Fermat's method
|
||||
// (exponentiation modulo P - 2, per Euler's theorem). This has better
|
||||
// constant-time properties than Euclid's method (implemented in
|
||||
// math/big.Int.ModInverse and FIPS 186-4, Appendix C.1) although math/big
|
||||
// itself isn't strictly constant-time so it's not perfect.
|
||||
func fermatInverse(k, N *big.Int) *big.Int {
|
||||
two := big.NewInt(2)
|
||||
nMinus2 := new(big.Int).Sub(N, two)
|
||||
@ -195,11 +189,22 @@ var errZeroParam = errors.New("zero parameter")
|
||||
// Sign signs a hash (which should be the result of hashing a larger message)
|
||||
// using the private key, priv. If the hash is longer than the bit-length of the
|
||||
// private key's curve order, the hash will be truncated to that length. It
|
||||
// returns the signature as a pair of integers. The security of the private key
|
||||
// depends on the entropy of rand.
|
||||
// returns the signature as a pair of integers. Most applications should use
|
||||
// SignASN1 instead of dealing directly with r, s.
|
||||
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
|
||||
randutil.MaybeReadByte(rand)
|
||||
|
||||
// This implementation derives the nonce from an AES-CTR CSPRNG keyed by:
|
||||
//
|
||||
// SHA2-512(priv.D || entropy || hash)[:32]
|
||||
//
|
||||
// The CSPRNG key is indifferentiable from a random oracle as shown in
|
||||
// [Coron], the AES-CTR stream is indifferentiable from a random oracle
|
||||
// under standard cryptographic assumptions (see [Larsson] for examples).
|
||||
//
|
||||
// [Coron]: https://cs.nyu.edu/~dodis/ps/merkle.pdf
|
||||
// [Larsson]: https://web.archive.org/web/20040719170906/https://www.nada.kth.se/kurser/kth/2D1441/semteo03/lecturenotes/assump.pdf
|
||||
|
||||
// Get 256 bits of entropy from rand.
|
||||
entropy := make([]byte, 32)
|
||||
_, err = io.ReadFull(rand, entropy)
|
||||
@ -207,7 +212,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
|
||||
return
|
||||
}
|
||||
|
||||
// Initialize an SHA-512 hash context; digest ...
|
||||
// Initialize an SHA-512 hash context; digest...
|
||||
md := sha512.New()
|
||||
md.Write(priv.D.Bytes()) // the private key,
|
||||
md.Write(entropy) // the entropy,
|
||||
@ -228,12 +233,12 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
|
||||
S: cipher.NewCTR(block, []byte(aesIV)),
|
||||
}
|
||||
|
||||
// See [NSA] 3.4.1
|
||||
c := priv.PublicKey.Curve
|
||||
return sign(priv, &csprng, c, hash)
|
||||
}
|
||||
|
||||
func signGeneric(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, hash []byte) (r, s *big.Int, err error) {
|
||||
// SEC 1, Version 2.0, Section 4.1.3
|
||||
N := c.Params().N
|
||||
if N.Sign() == 0 {
|
||||
return nil, nil, errZeroParam
|
||||
@ -276,16 +281,15 @@ func signGeneric(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve
|
||||
// SignASN1 signs a hash (which should be the result of hashing a larger message)
|
||||
// using the private key, priv. If the hash is longer than the bit-length of the
|
||||
// private key's curve order, the hash will be truncated to that length. It
|
||||
// returns the ASN.1 encoded signature. The security of the private key
|
||||
// depends on the entropy of rand.
|
||||
// returns the ASN.1 encoded signature.
|
||||
func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
|
||||
return priv.Sign(rand, hash, nil)
|
||||
}
|
||||
|
||||
// Verify verifies the signature in r, s of hash using the public key, pub. Its
|
||||
// return value records whether the signature is valid.
|
||||
// return value records whether the signature is valid. Most applications should
|
||||
// use VerifyASN1 instead of dealing directly with r, s.
|
||||
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
|
||||
// See [NSA] 3.4.2
|
||||
c := pub.Curve
|
||||
N := c.Params().N
|
||||
|
||||
@ -299,6 +303,7 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
|
||||
}
|
||||
|
||||
func verifyGeneric(pub *PublicKey, c elliptic.Curve, hash []byte, r, s *big.Int) bool {
|
||||
// SEC 1, Version 2.0, Section 4.1.4
|
||||
e := hashToInt(hash, c)
|
||||
var w *big.Int
|
||||
N := c.Params().N
|
||||
|
@ -2,17 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package elliptic implements several standard elliptic curves over prime
|
||||
// fields.
|
||||
// Package elliptic implements the standard NIST P-224, P-256, P-384, and P-521
|
||||
// elliptic curves over prime fields.
|
||||
package elliptic
|
||||
|
||||
// This package operates, internally, on Jacobian coordinates. For a given
|
||||
// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1)
|
||||
// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole
|
||||
// calculation can be performed within the transform (as in ScalarMult and
|
||||
// ScalarBaseMult). But even for Add and Double, it's faster to apply and
|
||||
// reverse the transform than to operate in affine coordinates.
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math/big"
|
||||
@ -21,12 +14,12 @@ import (
|
||||
|
||||
// A Curve represents a short-form Weierstrass curve with a=-3.
|
||||
//
|
||||
// The output of Add, Double, and ScalarMult when the input is not a point on
|
||||
// The behavior of Add, Double, and ScalarMult when the input is not a point on
|
||||
// the curve is undefined.
|
||||
//
|
||||
// Note that the conventional point at infinity (0, 0) is not considered on the
|
||||
// curve, although it can be returned by Add, Double, ScalarMult, or
|
||||
// ScalarBaseMult (but not Unmarshal or UnmarshalCompressed).
|
||||
// ScalarBaseMult (but not the Unmarshal or UnmarshalCompressed functions).
|
||||
type Curve interface {
|
||||
// Params returns the parameters for the curve.
|
||||
Params() *CurveParams
|
||||
@ -67,6 +60,13 @@ func (curve *CurveParams) Params() *CurveParams {
|
||||
return curve
|
||||
}
|
||||
|
||||
// CurveParams operates, internally, on Jacobian coordinates. For a given
|
||||
// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1)
|
||||
// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole
|
||||
// calculation can be performed within the transform (as in ScalarMult and
|
||||
// ScalarBaseMult). But even for Add and Double, it's faster to apply and
|
||||
// reverse the transform than to operate in affine coordinates.
|
||||
|
||||
// polynomial returns x³ - 3x + b.
|
||||
func (curve *CurveParams) polynomial(x *big.Int) *big.Int {
|
||||
x3 := new(big.Int).Mul(x, x)
|
||||
@ -89,6 +89,11 @@ func (curve *CurveParams) IsOnCurve(x, y *big.Int) bool {
|
||||
return specific.IsOnCurve(x, y)
|
||||
}
|
||||
|
||||
if x.Sign() < 0 || x.Cmp(curve.P) >= 0 ||
|
||||
y.Sign() < 0 || y.Cmp(curve.P) >= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// y² = x³ - 3x + b
|
||||
y2 := new(big.Int).Mul(y, y)
|
||||
y2.Mod(y2, curve.P)
|
||||
@ -353,7 +358,8 @@ func GenerateKey(curve Curve, rand io.Reader) (priv []byte, x, y *big.Int, err e
|
||||
}
|
||||
|
||||
// Marshal converts a point on the curve into the uncompressed form specified in
|
||||
// section 4.3.6 of ANSI X9.62.
|
||||
// SEC 1, Version 2.0, Section 2.3.3. If the point is not on the curve (or is
|
||||
// the conventional point at infinity), the behavior is undefined.
|
||||
func Marshal(curve Curve, x, y *big.Int) []byte {
|
||||
byteLen := (curve.Params().BitSize + 7) / 8
|
||||
|
||||
@ -367,7 +373,8 @@ func Marshal(curve Curve, x, y *big.Int) []byte {
|
||||
}
|
||||
|
||||
// MarshalCompressed converts a point on the curve into the compressed form
|
||||
// specified in section 4.3.6 of ANSI X9.62.
|
||||
// specified in SEC 1, Version 2.0, Section 2.3.3. If the point is not on the
|
||||
// curve (or is the conventional point at infinity), the behavior is undefined.
|
||||
func MarshalCompressed(curve Curve, x, y *big.Int) []byte {
|
||||
byteLen := (curve.Params().BitSize + 7) / 8
|
||||
compressed := make([]byte, 1+byteLen)
|
||||
@ -376,9 +383,9 @@ func MarshalCompressed(curve Curve, x, y *big.Int) []byte {
|
||||
return compressed
|
||||
}
|
||||
|
||||
// Unmarshal converts a point, serialized by Marshal, into an x, y pair.
|
||||
// It is an error if the point is not in uncompressed form or is not on the curve.
|
||||
// On error, x = nil.
|
||||
// Unmarshal converts a point, serialized by Marshal, into an x, y pair. It is
|
||||
// an error if the point is not in uncompressed form, is not on the curve, or is
|
||||
// the point at infinity. On error, x = nil.
|
||||
func Unmarshal(curve Curve, data []byte) (x, y *big.Int) {
|
||||
byteLen := (curve.Params().BitSize + 7) / 8
|
||||
if len(data) != 1+2*byteLen {
|
||||
@ -399,9 +406,9 @@ func Unmarshal(curve Curve, data []byte) (x, y *big.Int) {
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalCompressed converts a point, serialized by MarshalCompressed, into an x, y pair.
|
||||
// It is an error if the point is not in compressed form or is not on the curve.
|
||||
// On error, x = nil.
|
||||
// UnmarshalCompressed converts a point, serialized by MarshalCompressed, into
|
||||
// an x, y pair. It is an error if the point is not in compressed form, is not
|
||||
// on the curve, or is the point at infinity. On error, x = nil.
|
||||
func UnmarshalCompressed(curve Curve, data []byte) (x, y *big.Int) {
|
||||
byteLen := (curve.Params().BitSize + 7) / 8
|
||||
if len(data) != 1+byteLen {
|
||||
|
@ -182,6 +182,61 @@ func testUnmarshalToLargeCoordinates(t *testing.T, curve Curve) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestInvalidCoordinates tests big.Int values that are not valid field elements
|
||||
// (negative or bigger than P). They are expected to return false from
|
||||
// IsOnCurve, all other behavior is undefined.
|
||||
func TestInvalidCoordinates(t *testing.T) {
|
||||
testAllCurves(t, testInvalidCoordinates)
|
||||
}
|
||||
|
||||
func testInvalidCoordinates(t *testing.T, curve Curve) {
|
||||
checkIsOnCurveFalse := func(name string, x, y *big.Int) {
|
||||
if curve.IsOnCurve(x, y) {
|
||||
t.Errorf("IsOnCurve(%s) unexpectedly returned true", name)
|
||||
}
|
||||
}
|
||||
|
||||
p := curve.Params().P
|
||||
_, x, y, _ := GenerateKey(curve, rand.Reader)
|
||||
xx, yy := new(big.Int), new(big.Int)
|
||||
|
||||
// Check if the sign is getting dropped.
|
||||
xx.Neg(x)
|
||||
checkIsOnCurveFalse("-x, y", xx, y)
|
||||
yy.Neg(y)
|
||||
checkIsOnCurveFalse("x, -y", x, yy)
|
||||
|
||||
// Check if negative values are reduced modulo P.
|
||||
xx.Sub(x, p)
|
||||
checkIsOnCurveFalse("x-P, y", xx, y)
|
||||
yy.Sub(y, p)
|
||||
checkIsOnCurveFalse("x, y-P", x, yy)
|
||||
|
||||
// Check if positive values are reduced modulo P.
|
||||
xx.Add(x, p)
|
||||
checkIsOnCurveFalse("x+P, y", xx, y)
|
||||
yy.Add(y, p)
|
||||
checkIsOnCurveFalse("x, y+P", x, yy)
|
||||
|
||||
// Check if the overflow is dropped.
|
||||
xx.Add(x, new(big.Int).Lsh(big.NewInt(1), 535))
|
||||
checkIsOnCurveFalse("x+2⁵³⁵, y", xx, y)
|
||||
yy.Add(y, new(big.Int).Lsh(big.NewInt(1), 535))
|
||||
checkIsOnCurveFalse("x, y+2⁵³⁵", x, yy)
|
||||
|
||||
// Check if P is treated like zero (if possible).
|
||||
// y^2 = x^3 - 3x + B
|
||||
// y = mod_sqrt(x^3 - 3x + B)
|
||||
// y = mod_sqrt(B) if x = 0
|
||||
// If there is no modsqrt, there is no point with x = 0, can't test x = P.
|
||||
if yy := new(big.Int).ModSqrt(curve.Params().B, p); yy != nil {
|
||||
if !curve.IsOnCurve(big.NewInt(0), yy) {
|
||||
t.Fatal("(0, mod_sqrt(B)) is not on the curve?")
|
||||
}
|
||||
checkIsOnCurveFalse("P, y", p, yy)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalCompressed(t *testing.T) {
|
||||
t.Run("P-256/03", func(t *testing.T) {
|
||||
data, _ := hex.DecodeString("031e3987d9f9ea9d7dd7155a56a86b2009e1e0ab332f962d10d8beb6406ab1ad79")
|
||||
|
@ -7,30 +7,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/elliptic"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
buf := new(bytes.Buffer)
|
||||
fmt.Fprint(buf, `
|
||||
// Copyright 2021 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.
|
||||
|
||||
// Generated by gen_p256_table.go. DO NOT EDIT.
|
||||
|
||||
//go:build amd64 || arm64
|
||||
|
||||
package elliptic
|
||||
|
||||
`[1:])
|
||||
|
||||
// Generate precomputed p256 tables.
|
||||
var pre [43][32 * 8]uint64
|
||||
basePoint := []uint64{
|
||||
@ -70,41 +53,21 @@ package elliptic
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprint(buf, "const p256Precomputed = \"\" +\n\n")
|
||||
var bin []byte
|
||||
|
||||
// Dump the precomputed tables, flattened, little-endian.
|
||||
// These tables are used directly by assembly on little-endian platforms.
|
||||
// Putting the data in a const string lets it be stored readonly.
|
||||
// go:embedding the data into a string lets it be stored readonly.
|
||||
for i := range &pre {
|
||||
for j, v := range &pre[i] {
|
||||
fmt.Fprintf(buf, "\"")
|
||||
for _, v := range &pre[i] {
|
||||
var u8 [8]byte
|
||||
binary.LittleEndian.PutUint64(u8[:], v)
|
||||
for _, b := range &u8 {
|
||||
fmt.Fprintf(buf, "\\x%02x", b)
|
||||
}
|
||||
fmt.Fprintf(buf, "\"")
|
||||
if i < len(pre)-1 || j < len(pre[i])-1 {
|
||||
fmt.Fprint(buf, "+")
|
||||
}
|
||||
if j%8 == 7 {
|
||||
fmt.Fprint(buf, "\n")
|
||||
}
|
||||
bin = append(bin, u8[:]...)
|
||||
}
|
||||
fmt.Fprint(buf, "\n")
|
||||
}
|
||||
|
||||
src := buf.Bytes()
|
||||
fmtsrc, fmterr := format.Source(src)
|
||||
// If formatting failed, keep the original source for debugging.
|
||||
if fmterr == nil {
|
||||
src = fmtsrc
|
||||
}
|
||||
err := os.WriteFile("p256_asm_table.go", src, 0644)
|
||||
err := os.WriteFile("p256_asm_table.bin", bin, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if fmterr != nil {
|
||||
log.Fatal(fmterr)
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,9 @@ func p224PointFromAffine(x, y *big.Int) (p *nistec.P224Point, ok bool) {
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return nistec.NewP224Point(), true
|
||||
}
|
||||
if x.Sign() < 0 || y.Sign() < 0 {
|
||||
return nil, false
|
||||
}
|
||||
if x.BitLen() > 224 || y.BitLen() > 224 {
|
||||
return nil, false
|
||||
}
|
||||
|
@ -6,11 +6,11 @@
|
||||
|
||||
package elliptic
|
||||
|
||||
// This file contains a constant-time, 32-bit implementation of P256.
|
||||
// P-256 is implemented by various different backends, including a generic
|
||||
// 32-bit constant-time one in this file, which is used when assembly
|
||||
// implementations are not available, or not appropriate for the hardware.
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
)
|
||||
import "math/big"
|
||||
|
||||
type p256Curve struct {
|
||||
*CurveParams
|
||||
|
@ -15,11 +15,15 @@
|
||||
package elliptic
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
//go:generate go run -tags=tablegen gen_p256_table.go
|
||||
|
||||
//go:embed p256_asm_table.bin
|
||||
var p256Precomputed string
|
||||
|
||||
type (
|
||||
p256Curve struct {
|
||||
*CurveParams
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -66,6 +66,9 @@ func p384PointFromAffine(x, y *big.Int) (p *nistec.P384Point, ok bool) {
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return nistec.NewP384Point(), true
|
||||
}
|
||||
if x.Sign() < 0 || y.Sign() < 0 {
|
||||
return nil, false
|
||||
}
|
||||
if x.BitLen() > 384 || y.BitLen() > 384 {
|
||||
return nil, false
|
||||
}
|
||||
|
@ -71,6 +71,9 @@ func p521PointFromAffine(x, y *big.Int) (p *nistec.P521Point, ok bool) {
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return nistec.NewP521Point(), true
|
||||
}
|
||||
if x.Sign() < 0 || y.Sign() < 0 {
|
||||
return nil, false
|
||||
}
|
||||
if x.BitLen() > 521 || y.BitLen() > 521 {
|
||||
return nil, false
|
||||
}
|
||||
|
@ -51,9 +51,9 @@ func isPrintable(b byte) bool {
|
||||
}
|
||||
|
||||
// parseASN1String parses the ASN.1 string types T61String, PrintableString,
|
||||
// UTF8String, BMPString, and IA5String. This is mostly copied from the
|
||||
// respective encoding/asn1.parse... methods, rather than just increasing
|
||||
// the API surface of that package.
|
||||
// UTF8String, BMPString, IA5String, and NumericString. This is mostly copied
|
||||
// from the respective encoding/asn1.parse... methods, rather than just
|
||||
// increasing the API surface of that package.
|
||||
func parseASN1String(tag cryptobyte_asn1.Tag, value []byte) (string, error) {
|
||||
switch tag {
|
||||
case cryptobyte_asn1.T61String:
|
||||
@ -93,6 +93,13 @@ func parseASN1String(tag cryptobyte_asn1.Tag, value []byte) (string, error) {
|
||||
return "", errors.New("invalid IA5String")
|
||||
}
|
||||
return s, nil
|
||||
case cryptobyte_asn1.Tag(asn1.TagNumericString):
|
||||
for _, b := range value {
|
||||
if !('0' <= b && b <= '9' || b == ' ') {
|
||||
return "", errors.New("invalid NumericString")
|
||||
}
|
||||
}
|
||||
return string(value), nil
|
||||
}
|
||||
return "", fmt.Errorf("unsupported string type: %v", tag)
|
||||
}
|
||||
|
102
libgo/go/crypto/x509/parser_test.go
Normal file
102
libgo/go/crypto/x509/parser_test.go
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2021 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 x509
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"testing"
|
||||
|
||||
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
|
||||
)
|
||||
|
||||
func TestParseASN1String(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
tag cryptobyte_asn1.Tag
|
||||
value []byte
|
||||
expected string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "T61String",
|
||||
tag: cryptobyte_asn1.T61String,
|
||||
value: []byte{80, 81, 82},
|
||||
expected: string("PQR"),
|
||||
},
|
||||
{
|
||||
name: "PrintableString",
|
||||
tag: cryptobyte_asn1.PrintableString,
|
||||
value: []byte{80, 81, 82},
|
||||
expected: string("PQR"),
|
||||
},
|
||||
{
|
||||
name: "PrintableString (invalid)",
|
||||
tag: cryptobyte_asn1.PrintableString,
|
||||
value: []byte{1, 2, 3},
|
||||
expectedErr: "invalid PrintableString",
|
||||
},
|
||||
{
|
||||
name: "UTF8String",
|
||||
tag: cryptobyte_asn1.UTF8String,
|
||||
value: []byte{80, 81, 82},
|
||||
expected: string("PQR"),
|
||||
},
|
||||
{
|
||||
name: "UTF8String (invalid)",
|
||||
tag: cryptobyte_asn1.UTF8String,
|
||||
value: []byte{255},
|
||||
expectedErr: "invalid UTF-8 string",
|
||||
},
|
||||
{
|
||||
name: "BMPString",
|
||||
tag: cryptobyte_asn1.Tag(asn1.TagBMPString),
|
||||
value: []byte{80, 81},
|
||||
expected: string("偑"),
|
||||
},
|
||||
{
|
||||
name: "BMPString (invalid length)",
|
||||
tag: cryptobyte_asn1.Tag(asn1.TagBMPString),
|
||||
value: []byte{255},
|
||||
expectedErr: "invalid BMPString",
|
||||
},
|
||||
{
|
||||
name: "IA5String",
|
||||
tag: cryptobyte_asn1.IA5String,
|
||||
value: []byte{80, 81},
|
||||
expected: string("PQ"),
|
||||
},
|
||||
{
|
||||
name: "IA5String (invalid)",
|
||||
tag: cryptobyte_asn1.IA5String,
|
||||
value: []byte{255},
|
||||
expectedErr: "invalid IA5String",
|
||||
},
|
||||
{
|
||||
name: "NumericString",
|
||||
tag: cryptobyte_asn1.Tag(asn1.TagNumericString),
|
||||
value: []byte{49, 50},
|
||||
expected: string("12"),
|
||||
},
|
||||
{
|
||||
name: "NumericString (invalid)",
|
||||
tag: cryptobyte_asn1.Tag(asn1.TagNumericString),
|
||||
value: []byte{80},
|
||||
expectedErr: "invalid NumericString",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
out, err := parseASN1String(tc.tag, tc.value)
|
||||
if err != nil && err.Error() != tc.expectedErr {
|
||||
t.Fatalf("parseASN1String returned unexpected error: got %q, want %q", err, tc.expectedErr)
|
||||
} else if err == nil && tc.expectedErr != "" {
|
||||
t.Fatalf("parseASN1String didn't fail, expected: %s", tc.expectedErr)
|
||||
}
|
||||
if out != tc.expected {
|
||||
t.Fatalf("parseASN1String returned unexpected value: got %q, want %q", out, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -676,6 +676,9 @@ func (c *fakeConn) PrepareContext(ctx context.Context, query string) (driver.Stm
|
||||
|
||||
if c.waiter != nil {
|
||||
c.waiter(ctx)
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if stmt.wait > 0 {
|
||||
|
@ -418,26 +418,31 @@ func TestQueryContextWait(t *testing.T) {
|
||||
defer closeDB(t, db)
|
||||
prepares0 := numPrepares(t, db)
|
||||
|
||||
// TODO(kardianos): convert this from using a timeout to using an explicit
|
||||
// cancel when the query signals that it is "executing" the query.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// This will trigger the *fakeConn.Prepare method which will take time
|
||||
// performing the query. The ctxDriverPrepare func will check the context
|
||||
// after this and close the rows and return an error.
|
||||
_, err := db.QueryContext(ctx, "WAIT|1s|SELECT|people|age,name|")
|
||||
if err != context.DeadlineExceeded {
|
||||
c, err := db.Conn(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
c.dc.ci.(*fakeConn).waiter = func(c context.Context) {
|
||||
cancel()
|
||||
<-ctx.Done()
|
||||
}
|
||||
_, err = c.QueryContext(ctx, "SELECT|people|age,name|")
|
||||
c.Close()
|
||||
if err != context.Canceled {
|
||||
t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
|
||||
}
|
||||
|
||||
// Verify closed rows connection after error condition.
|
||||
waitForFree(t, db, 1)
|
||||
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
|
||||
// TODO(kardianos): if the context timeouts before the db.QueryContext
|
||||
// executes this check may fail. After adjusting how the context
|
||||
// is canceled above revert this back to a Fatal error.
|
||||
t.Logf("executed %d Prepare statements; want 1", prepares)
|
||||
t.Fatalf("executed %d Prepare statements; want 1", prepares)
|
||||
}
|
||||
}
|
||||
|
||||
@ -455,14 +460,14 @@ func TestTxContextWait(t *testing.T) {
|
||||
}
|
||||
tx.keepConnOnRollback = false
|
||||
|
||||
go func() {
|
||||
time.Sleep(15 * time.Millisecond)
|
||||
tx.dc.ci.(*fakeConn).waiter = func(c context.Context) {
|
||||
cancel()
|
||||
}()
|
||||
<-ctx.Done()
|
||||
}
|
||||
// This will trigger the *fakeConn.Prepare method which will take time
|
||||
// performing the query. The ctxDriverPrepare func will check the context
|
||||
// after this and close the rows and return an error.
|
||||
_, err = tx.QueryContext(ctx, "WAIT|1s|SELECT|people|age,name|")
|
||||
_, err = tx.QueryContext(ctx, "SELECT|people|age,name|")
|
||||
if err != context.Canceled {
|
||||
t.Fatalf("expected QueryContext to error with context canceled but returned %v", err)
|
||||
}
|
||||
|
@ -75,8 +75,8 @@ func Read(r io.ReaderAt) (*BuildInfo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bi := &BuildInfo{}
|
||||
if err := bi.UnmarshalText([]byte(mod)); err != nil {
|
||||
bi, err := debug.ParseBuildInfo(mod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bi.GoVersion = vers
|
||||
|
@ -212,12 +212,10 @@ func TestReadFile(t *testing.T) {
|
||||
} else {
|
||||
if tc.wantErr != "" {
|
||||
t.Fatalf("unexpected success; want error containing %q", tc.wantErr)
|
||||
} else if got, err := info.MarshalText(); err != nil {
|
||||
t.Fatalf("unexpected error marshaling BuildInfo: %v", err)
|
||||
} else if got := cleanOutputForComparison(string(got)); got != tc.want {
|
||||
if got != tc.want {
|
||||
t.Fatalf("got:\n%s\nwant:\n%s", got, tc.want)
|
||||
}
|
||||
}
|
||||
got := info.String()
|
||||
if clean := cleanOutputForComparison(string(got)); got != tc.want && clean != tc.want {
|
||||
t.Fatalf("got:\n%s\nwant:\n%s", got, tc.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -88,6 +88,7 @@ var depsRules = `
|
||||
< internal/itoa
|
||||
< internal/unsafeheader
|
||||
< runtime/internal/sys
|
||||
< runtime/internal/syscall
|
||||
< runtime/internal/atomic
|
||||
< runtime/internal/math
|
||||
< runtime
|
||||
@ -418,7 +419,7 @@ var depsRules = `
|
||||
CGO, fmt, net !< CRYPTO;
|
||||
|
||||
# CRYPTO-MATH is core bignum-based crypto - no cgo, net; fmt now ok.
|
||||
CRYPTO, FMT, math/big
|
||||
CRYPTO, FMT, math/big, embed
|
||||
< crypto/rand
|
||||
< crypto/internal/randutil
|
||||
< crypto/ed25519
|
||||
|
@ -927,6 +927,7 @@ var predeclaredTypes = map[string]bool{
|
||||
"any": true,
|
||||
"bool": true,
|
||||
"byte": true,
|
||||
"comparable": true,
|
||||
"complex64": true,
|
||||
"complex128": true,
|
||||
"error": true,
|
||||
|
3
libgo/go/go/doc/testdata/b.0.golden
vendored
3
libgo/go/go/doc/testdata/b.0.golden
vendored
@ -46,6 +46,9 @@ VARIABLES
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
// Associated with comparable type if AllDecls is set.
|
||||
func ComparableFactory() comparable
|
||||
|
||||
//
|
||||
func F(x int) int
|
||||
|
||||
|
6
libgo/go/go/doc/testdata/b.1.golden
vendored
6
libgo/go/go/doc/testdata/b.1.golden
vendored
@ -38,6 +38,12 @@ TYPES
|
||||
//
|
||||
func (x *T) M()
|
||||
|
||||
// Should only appear if AllDecls is set.
|
||||
type comparable struct{} // overrides a predeclared type comparable
|
||||
|
||||
// Associated with comparable type if AllDecls is set.
|
||||
func ComparableFactory() comparable
|
||||
|
||||
//
|
||||
type notExported int
|
||||
|
||||
|
3
libgo/go/go/doc/testdata/b.2.golden
vendored
3
libgo/go/go/doc/testdata/b.2.golden
vendored
@ -46,6 +46,9 @@ VARIABLES
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
// Associated with comparable type if AllDecls is set.
|
||||
func ComparableFactory() comparable
|
||||
|
||||
//
|
||||
func F(x int) int
|
||||
|
||||
|
6
libgo/go/go/doc/testdata/b.go
vendored
6
libgo/go/go/doc/testdata/b.go
vendored
@ -27,9 +27,15 @@ func UintFactory() uint {}
|
||||
// Associated with uint type if AllDecls is set.
|
||||
func uintFactory() uint {}
|
||||
|
||||
// Associated with comparable type if AllDecls is set.
|
||||
func ComparableFactory() comparable {}
|
||||
|
||||
// Should only appear if AllDecls is set.
|
||||
type uint struct{} // overrides a predeclared type uint
|
||||
|
||||
// Should only appear if AllDecls is set.
|
||||
type comparable struct{} // overrides a predeclared type comparable
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Exported declarations associated with non-exported types must always be shown.
|
||||
|
||||
|
@ -543,6 +543,13 @@ func (p *parser) parseArrayType(lbrack token.Pos, len ast.Expr) *ast.ArrayType {
|
||||
}
|
||||
p.exprLev--
|
||||
}
|
||||
if p.tok == token.COMMA {
|
||||
// Trailing commas are accepted in type parameter
|
||||
// lists but not in array type declarations.
|
||||
// Accept for better error handling but complain.
|
||||
p.error(p.pos, "unexpected comma; expecting ]")
|
||||
p.next()
|
||||
}
|
||||
p.expect(token.RBRACK)
|
||||
elt := p.parseType()
|
||||
return &ast.ArrayType{Lbrack: lbrack, Len: len, Elt: elt}
|
||||
@ -797,7 +804,7 @@ func (p *parser) parseParamDecl(name *ast.Ident, typeSetsOK bool) (f field) {
|
||||
return
|
||||
}
|
||||
|
||||
func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token) (params []*ast.Field) {
|
||||
func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing token.Token) (params []*ast.Field) {
|
||||
if p.trace {
|
||||
defer un(trace(p, "ParameterList"))
|
||||
}
|
||||
@ -816,8 +823,17 @@ func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token) (para
|
||||
var named int // number of parameters that have an explicit name and type
|
||||
|
||||
for name0 != nil || p.tok != closing && p.tok != token.EOF {
|
||||
par := p.parseParamDecl(name0, typeSetsOK)
|
||||
var par field
|
||||
if typ0 != nil {
|
||||
if typeSetsOK {
|
||||
typ0 = p.embeddedElem(typ0)
|
||||
}
|
||||
par = field{name0, typ0}
|
||||
} else {
|
||||
par = p.parseParamDecl(name0, typeSetsOK)
|
||||
}
|
||||
name0 = nil // 1st name was consumed if present
|
||||
typ0 = nil // 1st typ was consumed if present
|
||||
if par.name != nil || par.typ != nil {
|
||||
list = append(list, par)
|
||||
if par.name != nil && par.typ != nil {
|
||||
@ -926,7 +942,7 @@ func (p *parser) parseParameters(acceptTParams bool) (tparams, params *ast.Field
|
||||
opening := p.pos
|
||||
p.next()
|
||||
// [T any](params) syntax
|
||||
list := p.parseParameterList(nil, token.RBRACK)
|
||||
list := p.parseParameterList(nil, nil, token.RBRACK)
|
||||
rbrack := p.expect(token.RBRACK)
|
||||
tparams = &ast.FieldList{Opening: opening, List: list, Closing: rbrack}
|
||||
// Type parameter lists must not be empty.
|
||||
@ -940,7 +956,7 @@ func (p *parser) parseParameters(acceptTParams bool) (tparams, params *ast.Field
|
||||
|
||||
var fields []*ast.Field
|
||||
if p.tok != token.RPAREN {
|
||||
fields = p.parseParameterList(nil, token.RPAREN)
|
||||
fields = p.parseParameterList(nil, nil, token.RPAREN)
|
||||
}
|
||||
|
||||
rparen := p.expect(token.RPAREN)
|
||||
@ -977,7 +993,7 @@ func (p *parser) parseFuncType() *ast.FuncType {
|
||||
pos := p.expect(token.FUNC)
|
||||
tparams, params := p.parseParameters(true)
|
||||
if tparams != nil {
|
||||
p.error(tparams.Pos(), "function type cannot have type parameters")
|
||||
p.error(tparams.Pos(), "function type must have no type parameters")
|
||||
}
|
||||
results := p.parseResult()
|
||||
|
||||
@ -1004,18 +1020,21 @@ func (p *parser) parseMethodSpec() *ast.Field {
|
||||
p.exprLev--
|
||||
if name0, _ := x.(*ast.Ident); name0 != nil && p.tok != token.COMMA && p.tok != token.RBRACK {
|
||||
// generic method m[T any]
|
||||
list := p.parseParameterList(name0, token.RBRACK)
|
||||
rbrack := p.expect(token.RBRACK)
|
||||
tparams := &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack}
|
||||
//
|
||||
// Interface methods do not have type parameters. We parse them for a
|
||||
// better error message and improved error recovery.
|
||||
_ = p.parseParameterList(name0, nil, token.RBRACK)
|
||||
_ = p.expect(token.RBRACK)
|
||||
p.error(lbrack, "interface method must have no type parameters")
|
||||
|
||||
// TODO(rfindley) refactor to share code with parseFuncType.
|
||||
_, params := p.parseParameters(false)
|
||||
results := p.parseResult()
|
||||
idents = []*ast.Ident{ident}
|
||||
typ = &ast.FuncType{
|
||||
Func: token.NoPos,
|
||||
TypeParams: tparams,
|
||||
Params: params,
|
||||
Results: results,
|
||||
Func: token.NoPos,
|
||||
Params: params,
|
||||
Results: results,
|
||||
}
|
||||
} else {
|
||||
// embedded instantiated type
|
||||
@ -1781,7 +1800,12 @@ func (p *parser) tokPrec() (token.Token, int) {
|
||||
return tok, tok.Precedence()
|
||||
}
|
||||
|
||||
func (p *parser) parseBinaryExpr(x ast.Expr, prec1 int) ast.Expr {
|
||||
// parseBinaryExpr parses a (possibly) binary expression.
|
||||
// If x is non-nil, it is used as the left operand.
|
||||
// If check is true, operands are checked to be valid expressions.
|
||||
//
|
||||
// TODO(rfindley): parseBinaryExpr has become overloaded. Consider refactoring.
|
||||
func (p *parser) parseBinaryExpr(x ast.Expr, prec1 int, check bool) ast.Expr {
|
||||
if p.trace {
|
||||
defer un(trace(p, "BinaryExpr"))
|
||||
}
|
||||
@ -1795,11 +1819,32 @@ func (p *parser) parseBinaryExpr(x ast.Expr, prec1 int) ast.Expr {
|
||||
return x
|
||||
}
|
||||
pos := p.expect(op)
|
||||
y := p.parseBinaryExpr(nil, oprec+1)
|
||||
x = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)}
|
||||
y := p.parseBinaryExpr(nil, oprec+1, check)
|
||||
if check {
|
||||
x = p.checkExpr(x)
|
||||
y = p.checkExpr(y)
|
||||
}
|
||||
x = &ast.BinaryExpr{X: x, OpPos: pos, Op: op, Y: y}
|
||||
}
|
||||
}
|
||||
|
||||
// checkBinaryExpr checks binary expressions that were not already checked by
|
||||
// parseBinaryExpr, because the latter was called with check=false.
|
||||
func (p *parser) checkBinaryExpr(x ast.Expr) {
|
||||
bx, ok := x.(*ast.BinaryExpr)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
bx.X = p.checkExpr(bx.X)
|
||||
bx.Y = p.checkExpr(bx.Y)
|
||||
|
||||
// parseBinaryExpr checks x and y for each binary expr in a tree, so we
|
||||
// traverse the tree of binary exprs starting from x.
|
||||
p.checkBinaryExpr(bx.X)
|
||||
p.checkBinaryExpr(bx.Y)
|
||||
}
|
||||
|
||||
// The result may be a type or even a raw type ([...]int). Callers must
|
||||
// check the result (using checkExpr or checkExprOrType), depending on
|
||||
// context.
|
||||
@ -1808,7 +1853,7 @@ func (p *parser) parseExpr() ast.Expr {
|
||||
defer un(trace(p, "Expression"))
|
||||
}
|
||||
|
||||
return p.parseBinaryExpr(nil, token.LowestPrec+1)
|
||||
return p.parseBinaryExpr(nil, token.LowestPrec+1, true)
|
||||
}
|
||||
|
||||
func (p *parser) parseRhs() ast.Expr {
|
||||
@ -2531,12 +2576,12 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, _ token.Pos, keyword toke
|
||||
return spec
|
||||
}
|
||||
|
||||
func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *ast.Ident) {
|
||||
func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *ast.Ident, typ0 ast.Expr) {
|
||||
if p.trace {
|
||||
defer un(trace(p, "parseGenericType"))
|
||||
}
|
||||
|
||||
list := p.parseParameterList(name0, token.RBRACK)
|
||||
list := p.parseParameterList(name0, typ0, token.RBRACK)
|
||||
closePos := p.expect(token.RBRACK)
|
||||
spec.TypeParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos}
|
||||
// Let the type checker decide whether to accept type parameters on aliases:
|
||||
@ -2561,31 +2606,85 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
|
||||
lbrack := p.pos
|
||||
p.next()
|
||||
if p.tok == token.IDENT {
|
||||
// array type or generic type: [name0...
|
||||
name0 := p.parseIdent()
|
||||
// We may have an array type or a type parameter list.
|
||||
// In either case we expect an expression x (which may
|
||||
// just be a name, or a more complex expression) which
|
||||
// we can analyze further.
|
||||
//
|
||||
// A type parameter list may have a type bound starting
|
||||
// with a "[" as in: P []E. In that case, simply parsing
|
||||
// an expression would lead to an error: P[] is invalid.
|
||||
// But since index or slice expressions are never constant
|
||||
// and thus invalid array length expressions, if we see a
|
||||
// "[" following a name it must be the start of an array
|
||||
// or slice constraint. Only if we don't see a "[" do we
|
||||
// need to parse a full expression.
|
||||
|
||||
// Index or slice expressions are never constant and thus invalid
|
||||
// array length expressions. Thus, if we see a "[" following name
|
||||
// we can safely assume that "[" name starts a type parameter list.
|
||||
var x ast.Expr // x != nil means x is the array length expression
|
||||
var x ast.Expr = p.parseIdent()
|
||||
if p.tok != token.LBRACK {
|
||||
// We may still have either an array type or generic type -- check if
|
||||
// name0 is the entire expr.
|
||||
// To parse the expression starting with name, expand
|
||||
// the call sequence we would get by passing in name
|
||||
// to parser.expr, and pass in name to parsePrimaryExpr.
|
||||
p.exprLev++
|
||||
lhs := p.parsePrimaryExpr(name0)
|
||||
x = p.parseBinaryExpr(lhs, token.LowestPrec+1)
|
||||
lhs := p.parsePrimaryExpr(x)
|
||||
x = p.parseBinaryExpr(lhs, token.LowestPrec+1, false)
|
||||
p.exprLev--
|
||||
if x == name0 && p.tok != token.RBRACK {
|
||||
x = nil
|
||||
}
|
||||
|
||||
// analyze the cases
|
||||
var pname *ast.Ident // pname != nil means pname is the type parameter name
|
||||
var ptype ast.Expr // ptype != nil means ptype is the type parameter type; pname != nil in this case
|
||||
|
||||
switch t := x.(type) {
|
||||
case *ast.Ident:
|
||||
// Unless we see a "]", we are at the start of a type parameter list.
|
||||
if p.tok != token.RBRACK {
|
||||
// d.Name "[" name ...
|
||||
pname = t
|
||||
// no ptype
|
||||
}
|
||||
case *ast.BinaryExpr:
|
||||
// If we have an expression of the form name*T, and T is a (possibly
|
||||
// parenthesized) type literal or the next token is a comma, we are
|
||||
// at the start of a type parameter list.
|
||||
if name, _ := t.X.(*ast.Ident); name != nil {
|
||||
if t.Op == token.MUL && (isTypeLit(t.Y) || p.tok == token.COMMA) {
|
||||
// d.Name "[" name "*" t.Y
|
||||
// d.Name "[" name "*" t.Y ","
|
||||
// convert t into unary *t.Y
|
||||
pname = name
|
||||
ptype = &ast.StarExpr{Star: t.OpPos, X: t.Y}
|
||||
}
|
||||
}
|
||||
if pname == nil {
|
||||
// A normal binary expression. Since we passed check=false, we must
|
||||
// now check its operands.
|
||||
p.checkBinaryExpr(t)
|
||||
}
|
||||
case *ast.CallExpr:
|
||||
// If we have an expression of the form name(T), and T is a (possibly
|
||||
// parenthesized) type literal or the next token is a comma, we are
|
||||
// at the start of a type parameter list.
|
||||
if name, _ := t.Fun.(*ast.Ident); name != nil {
|
||||
if len(t.Args) == 1 && !t.Ellipsis.IsValid() && (isTypeLit(t.Args[0]) || p.tok == token.COMMA) {
|
||||
// d.Name "[" name "(" t.ArgList[0] ")"
|
||||
// d.Name "[" name "(" t.ArgList[0] ")" ","
|
||||
pname = name
|
||||
ptype = t.Args[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if x == nil {
|
||||
// generic type [T any];
|
||||
p.parseGenericType(spec, lbrack, name0)
|
||||
if pname != nil {
|
||||
// d.Name "[" pname ...
|
||||
// d.Name "[" pname ptype ...
|
||||
// d.Name "[" pname ptype "," ...
|
||||
p.parseGenericType(spec, lbrack, pname, ptype)
|
||||
} else {
|
||||
// array type
|
||||
// TODO(rfindley) should resolve all identifiers in x.
|
||||
// d.Name "[" x ...
|
||||
spec.Type = p.parseArrayType(lbrack, x)
|
||||
}
|
||||
} else {
|
||||
@ -2608,6 +2707,21 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
|
||||
return spec
|
||||
}
|
||||
|
||||
// isTypeLit reports whether x is a (possibly parenthesized) type literal.
|
||||
func isTypeLit(x ast.Expr) bool {
|
||||
switch x := x.(type) {
|
||||
case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
|
||||
return true
|
||||
case *ast.StarExpr:
|
||||
// *T may be a pointer dereferenciation.
|
||||
// Only consider *T as type literal if T is a type literal.
|
||||
return isTypeLit(x.X)
|
||||
case *ast.ParenExpr:
|
||||
return isTypeLit(x.X)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.GenDecl {
|
||||
if p.trace {
|
||||
defer un(trace(p, "GenDecl("+keyword.String()+")"))
|
||||
@ -2655,6 +2769,12 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
|
||||
ident := p.parseIdent()
|
||||
|
||||
tparams, params := p.parseParameters(true)
|
||||
if recv != nil && tparams != nil {
|
||||
// Method declarations do not have type parameters. We parse them for a
|
||||
// better error message and improved error recovery.
|
||||
p.error(tparams.Opening, "method must have no type parameters")
|
||||
tparams = nil
|
||||
}
|
||||
results := p.parseResult()
|
||||
|
||||
var body *ast.BlockStmt
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const debugResolve = false
|
||||
@ -24,6 +25,7 @@ func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, str
|
||||
declErr: declErr,
|
||||
topScope: pkgScope,
|
||||
pkgScope: pkgScope,
|
||||
depth: 1,
|
||||
}
|
||||
|
||||
for _, decl := range file.Decls {
|
||||
@ -45,7 +47,7 @@ func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, str
|
||||
i++
|
||||
} else if debugResolve {
|
||||
pos := ident.Obj.Decl.(interface{ Pos() token.Pos }).Pos()
|
||||
r.dump("resolved %s@%v to package object %v", ident.Name, ident.Pos(), pos)
|
||||
r.trace("resolved %s@%v to package object %v", ident.Name, ident.Pos(), pos)
|
||||
}
|
||||
}
|
||||
file.Scope = r.pkgScope
|
||||
@ -60,6 +62,7 @@ type resolver struct {
|
||||
pkgScope *ast.Scope // pkgScope.Outer == nil
|
||||
topScope *ast.Scope // top-most scope; may be pkgScope
|
||||
unresolved []*ast.Ident // unresolved identifiers
|
||||
depth int // scope depth
|
||||
|
||||
// Label scopes
|
||||
// (maintained by open/close LabelScope)
|
||||
@ -67,8 +70,8 @@ type resolver struct {
|
||||
targetStack [][]*ast.Ident // stack of unresolved labels
|
||||
}
|
||||
|
||||
func (r *resolver) dump(format string, args ...any) {
|
||||
fmt.Println(">>> " + r.sprintf(format, args...))
|
||||
func (r *resolver) trace(format string, args ...any) {
|
||||
fmt.Println(strings.Repeat(". ", r.depth) + r.sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (r *resolver) sprintf(format string, args ...any) string {
|
||||
@ -83,14 +86,16 @@ func (r *resolver) sprintf(format string, args ...any) string {
|
||||
|
||||
func (r *resolver) openScope(pos token.Pos) {
|
||||
if debugResolve {
|
||||
r.dump("opening scope @%v", pos)
|
||||
r.trace("opening scope @%v", pos)
|
||||
r.depth++
|
||||
}
|
||||
r.topScope = ast.NewScope(r.topScope)
|
||||
}
|
||||
|
||||
func (r *resolver) closeScope() {
|
||||
if debugResolve {
|
||||
r.dump("closing scope")
|
||||
r.depth--
|
||||
r.trace("closing scope")
|
||||
}
|
||||
r.topScope = r.topScope.Outer
|
||||
}
|
||||
@ -117,21 +122,27 @@ func (r *resolver) closeLabelScope() {
|
||||
|
||||
func (r *resolver) declare(decl, data any, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
|
||||
for _, ident := range idents {
|
||||
assert(ident.Obj == nil, "identifier already declared or resolved")
|
||||
if ident.Obj != nil {
|
||||
panic(fmt.Sprintf("%v: identifier %s already declared or resolved", ident.Pos(), ident.Name))
|
||||
}
|
||||
obj := ast.NewObj(kind, ident.Name)
|
||||
// remember the corresponding declaration for redeclaration
|
||||
// errors and global variable resolution/typechecking phase
|
||||
obj.Decl = decl
|
||||
obj.Data = data
|
||||
ident.Obj = obj
|
||||
// Identifiers (for receiver type parameters) are written to the scope, but
|
||||
// never set as the resolved object. See issue #50956.
|
||||
if _, ok := decl.(*ast.Ident); !ok {
|
||||
ident.Obj = obj
|
||||
}
|
||||
if ident.Name != "_" {
|
||||
if debugResolve {
|
||||
r.dump("declaring %s@%v", ident.Name, ident.Pos())
|
||||
r.trace("declaring %s@%v", ident.Name, ident.Pos())
|
||||
}
|
||||
if alt := scope.Insert(obj); alt != nil && r.declErr != nil {
|
||||
prevDecl := ""
|
||||
if pos := alt.Pos(); pos.IsValid() {
|
||||
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", r.handle.Position(pos))
|
||||
prevDecl = r.sprintf("\n\tprevious declaration at %v", pos)
|
||||
}
|
||||
r.declErr(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
|
||||
}
|
||||
@ -153,7 +164,7 @@ func (r *resolver) shortVarDecl(decl *ast.AssignStmt) {
|
||||
ident.Obj = obj
|
||||
if ident.Name != "_" {
|
||||
if debugResolve {
|
||||
r.dump("declaring %s@%v", ident.Name, ident.Pos())
|
||||
r.trace("declaring %s@%v", ident.Name, ident.Pos())
|
||||
}
|
||||
if alt := r.topScope.Insert(obj); alt != nil {
|
||||
ident.Obj = alt // redeclaration
|
||||
@ -180,7 +191,7 @@ var unresolved = new(ast.Object)
|
||||
//
|
||||
func (r *resolver) resolve(ident *ast.Ident, collectUnresolved bool) {
|
||||
if ident.Obj != nil {
|
||||
panic(fmt.Sprintf("%s: identifier %s already declared or resolved", r.handle.Position(ident.Pos()), ident.Name))
|
||||
panic(r.sprintf("%v: identifier %s already declared or resolved", ident.Pos(), ident.Name))
|
||||
}
|
||||
// '_' should never refer to existing declarations, because it has special
|
||||
// handling in the spec.
|
||||
@ -189,8 +200,15 @@ func (r *resolver) resolve(ident *ast.Ident, collectUnresolved bool) {
|
||||
}
|
||||
for s := r.topScope; s != nil; s = s.Outer {
|
||||
if obj := s.Lookup(ident.Name); obj != nil {
|
||||
if debugResolve {
|
||||
r.trace("resolved %v:%s to %v", ident.Pos(), ident.Name, obj)
|
||||
}
|
||||
assert(obj.Name != "", "obj with no name")
|
||||
ident.Obj = obj
|
||||
// Identifiers (for receiver type parameters) are written to the scope,
|
||||
// but never set as the resolved object. See issue #50956.
|
||||
if _, ok := obj.Decl.(*ast.Ident); !ok {
|
||||
ident.Obj = obj
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -227,7 +245,7 @@ func (r *resolver) walkStmts(list []ast.Stmt) {
|
||||
|
||||
func (r *resolver) Visit(node ast.Node) ast.Visitor {
|
||||
if debugResolve && node != nil {
|
||||
r.dump("node %T@%v", node, node.Pos())
|
||||
r.trace("node %T@%v", node, node.Pos())
|
||||
}
|
||||
|
||||
switch n := node.(type) {
|
||||
@ -461,8 +479,7 @@ func (r *resolver) Visit(node ast.Node) ast.Visitor {
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
|
||||
// Resolve the receiver first, without declaring.
|
||||
r.resolveList(n.Recv)
|
||||
r.walkRecv(n.Recv)
|
||||
|
||||
// Type parameters are walked normally: they can reference each other, and
|
||||
// can be referenced by normal parameters.
|
||||
@ -519,6 +536,52 @@ func (r *resolver) declareList(list *ast.FieldList, kind ast.ObjKind) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) walkRecv(recv *ast.FieldList) {
|
||||
// If our receiver has receiver type parameters, we must declare them before
|
||||
// trying to resolve the rest of the receiver, and avoid re-resolving the
|
||||
// type parameter identifiers.
|
||||
if recv == nil || len(recv.List) == 0 {
|
||||
return // nothing to do
|
||||
}
|
||||
typ := recv.List[0].Type
|
||||
if ptr, ok := typ.(*ast.StarExpr); ok {
|
||||
typ = ptr.X
|
||||
}
|
||||
|
||||
var declareExprs []ast.Expr // exprs to declare
|
||||
var resolveExprs []ast.Expr // exprs to resolve
|
||||
switch typ := typ.(type) {
|
||||
case *ast.IndexExpr:
|
||||
declareExprs = []ast.Expr{typ.Index}
|
||||
resolveExprs = append(resolveExprs, typ.X)
|
||||
case *ast.IndexListExpr:
|
||||
declareExprs = typ.Indices
|
||||
resolveExprs = append(resolveExprs, typ.X)
|
||||
default:
|
||||
resolveExprs = append(resolveExprs, typ)
|
||||
}
|
||||
for _, expr := range declareExprs {
|
||||
if id, _ := expr.(*ast.Ident); id != nil {
|
||||
r.declare(expr, nil, r.topScope, ast.Typ, id)
|
||||
} else {
|
||||
// The receiver type parameter expression is invalid, but try to resolve
|
||||
// it anyway for consistency.
|
||||
resolveExprs = append(resolveExprs, expr)
|
||||
}
|
||||
}
|
||||
for _, expr := range resolveExprs {
|
||||
if expr != nil {
|
||||
ast.Walk(r, expr)
|
||||
}
|
||||
}
|
||||
// The receiver is invalid, but try to resolve it anyway for consistency.
|
||||
for _, f := range recv.List[1:] {
|
||||
if f.Type != nil {
|
||||
ast.Walk(r, f.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) walkFieldList(list *ast.FieldList, kind ast.ObjKind) {
|
||||
if list == nil {
|
||||
return
|
||||
|
@ -74,7 +74,7 @@ var validWithTParamsOnly = []string{
|
||||
`package p; type T[P any /* ERROR "expected ']', found any" */ ] struct { P }`,
|
||||
`package p; type T[P comparable /* ERROR "expected ']', found comparable" */ ] struct { P }`,
|
||||
`package p; type T[P comparable /* ERROR "expected ']', found comparable" */ [P]] struct { P }`,
|
||||
`package p; type T[P1, /* ERROR "expected ']', found ','" */ P2 any] struct { P1; f []P2 }`,
|
||||
`package p; type T[P1, /* ERROR "unexpected comma" */ P2 any] struct { P1; f []P2 }`,
|
||||
`package p; func _[ /* ERROR "expected '\(', found '\['" */ T any]()()`,
|
||||
`package p; func _(T (P))`,
|
||||
`package p; func f[ /* ERROR "expected '\(', found '\['" */ A, B any](); func _() { _ = f[int, int] }`,
|
||||
@ -83,8 +83,8 @@ var validWithTParamsOnly = []string{
|
||||
`package p; func _(p.T[ /* ERROR "missing ',' in parameter list" */ Q])`,
|
||||
`package p; type _[A interface /* ERROR "expected ']', found 'interface'" */ {},] struct{}`,
|
||||
`package p; type _[A interface /* ERROR "expected ']', found 'interface'" */ {}] struct{}`,
|
||||
`package p; type _[A, /* ERROR "expected ']', found ','" */ B any,] struct{}`,
|
||||
`package p; type _[A, /* ERROR "expected ']', found ','" */ B any] struct{}`,
|
||||
`package p; type _[A, /* ERROR "unexpected comma" */ B any,] struct{}`,
|
||||
`package p; type _[A, /* ERROR "unexpected comma" */ B any] struct{}`,
|
||||
`package p; type _[A any /* ERROR "expected ']', found any" */,] struct{}`,
|
||||
`package p; type _[A any /* ERROR "expected ']', found any" */ ]struct{}`,
|
||||
`package p; type _[A any /* ERROR "expected ']', found any" */ ] struct{ A }`,
|
||||
@ -94,11 +94,9 @@ var validWithTParamsOnly = []string{
|
||||
`package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B any](a A) B`,
|
||||
`package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B C](a A) B`,
|
||||
`package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B C[A, B]](a A) B`,
|
||||
`package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B any](a A) B`,
|
||||
`package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C](a A) B`,
|
||||
`package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C[A, B]](a A) B`,
|
||||
`package p; type _[A, /* ERROR "expected ']', found ','" */ B any] interface { _(a A) B }`,
|
||||
`package p; type _[A, /* ERROR "expected ']', found ','" */ B C[A, B]] interface { _(a A) B }`,
|
||||
|
||||
`package p; type _[A, /* ERROR "unexpected comma" */ B any] interface { _(a A) B }`,
|
||||
`package p; type _[A, /* ERROR "unexpected comma" */ B C[A, B]] interface { _(a A) B }`,
|
||||
`package p; func _[ /* ERROR "expected '\(', found '\['" */ T1, T2 interface{}](x T1) T2`,
|
||||
`package p; func _[ /* ERROR "expected '\(', found '\['" */ T1 interface{ m() }, T2, T3 interface{}](x T1, y T3) T2`,
|
||||
`package p; var _ = [ /* ERROR "expected expression" */ ]T[int]{}`,
|
||||
@ -110,10 +108,10 @@ var validWithTParamsOnly = []string{
|
||||
`package p; var _ T[ /* ERROR "expected ';', found '\['" */ chan int]`,
|
||||
|
||||
// TODO(rfindley) this error message could be improved.
|
||||
`package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[P]) _[T any](x T)`,
|
||||
`package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[ P, Q]) _[T1, T2 any](x T)`,
|
||||
`package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[P]) _(x T)`,
|
||||
`package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[ P, Q]) _(x T)`,
|
||||
|
||||
`package p; func (R[P] /* ERROR "missing element type" */ ) _[T any]()`,
|
||||
`package p; func (R[P] /* ERROR "missing element type" */ ) _()`,
|
||||
`package p; func _(T[P] /* ERROR "missing element type" */ )`,
|
||||
`package p; func _(T[P1, /* ERROR "expected ']', found ','" */ P2, P3 ])`,
|
||||
`package p; func _(T[P] /* ERROR "missing element type" */ ) T[P]`,
|
||||
@ -122,7 +120,7 @@ var validWithTParamsOnly = []string{
|
||||
`package p; type _ interface{int| /* ERROR "expected ';'" */ float32; bool; m(); string;}`,
|
||||
`package p; type I1[T any /* ERROR "expected ']', found any" */ ] interface{}; type I2 interface{ I1[int] }`,
|
||||
`package p; type I1[T any /* ERROR "expected ']', found any" */ ] interface{}; type I2[T any] interface{ I1[T] }`,
|
||||
`package p; type _ interface { f[ /* ERROR "expected ';', found '\['" */ T any]() }`,
|
||||
`package p; type _ interface { N[ /* ERROR "expected ';', found '\['" */ T] }`,
|
||||
`package p; type T[P any /* ERROR "expected ']'" */ ] = T0`,
|
||||
}
|
||||
|
||||
@ -195,7 +193,7 @@ var invalids = []string{
|
||||
`package p; func f() { go func() { func() { f(x func /* ERROR "missing ','" */ (){}) } } }`,
|
||||
`package p; func _() (type /* ERROR "found 'type'" */ T)(T)`,
|
||||
`package p; func (type /* ERROR "found 'type'" */ T)(T) _()`,
|
||||
`package p; type _[A+B, /* ERROR "expected ']'" */ ] int`,
|
||||
`package p; type _[A+B, /* ERROR "unexpected comma" */ ] int`,
|
||||
|
||||
// TODO(rfindley): this error should be positioned on the ':'
|
||||
`package p; var a = a[[]int:[ /* ERROR "expected expression" */ ]int];`,
|
||||
@ -233,22 +231,34 @@ var invalidNoTParamErrs = []string{
|
||||
`package p; type T[P any /* ERROR "expected ']', found any" */ ] = T0`,
|
||||
`package p; var _ func[ /* ERROR "expected '\(', found '\['" */ T any](T)`,
|
||||
`package p; func _[ /* ERROR "expected '\(', found '\['" */ ]()`,
|
||||
`package p; type _[A, /* ERROR "expected ']', found ','" */] struct{ A }`,
|
||||
`package p; type _[A, /* ERROR "unexpected comma" */] struct{ A }`,
|
||||
`package p; func _[ /* ERROR "expected '\(', found '\['" */ type P, *Q interface{}]()`,
|
||||
|
||||
`package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B any](a A) B`,
|
||||
`package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C](a A) B`,
|
||||
`package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C[A, B]](a A) B`,
|
||||
|
||||
`package p; func(*T[ /* ERROR "missing ',' in parameter list" */ e, e]) _()`,
|
||||
}
|
||||
|
||||
// invalidTParamErrs holds invalid source code examples annotated with the
|
||||
// error messages produced when ParseTypeParams is set.
|
||||
var invalidTParamErrs = []string{
|
||||
`package p; type _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`,
|
||||
`package p; var _ func[ /* ERROR "cannot have type parameters" */ T any](T)`,
|
||||
`package p; var _ func[ /* ERROR "must have no type parameters" */ T any](T)`,
|
||||
`package p; func _[]/* ERROR "empty type parameter list" */()`,
|
||||
|
||||
// TODO(rfindley) a better location would be after the ']'
|
||||
`package p; type _[A/* ERROR "all type parameters must be named" */,] struct{ A }`,
|
||||
`package p; type _[A /* ERROR "all type parameters must be named" */ ,] struct{ A }`,
|
||||
|
||||
// TODO(rfindley) this error is confusing.
|
||||
`package p; func _[type /* ERROR "all type parameters must be named" */P, *Q interface{}]()`,
|
||||
`package p; func _[type /* ERROR "all type parameters must be named" */ P, *Q interface{}]()`,
|
||||
|
||||
`package p; func (T) _[ /* ERROR "must have no type parameters" */ A, B any](a A) B`,
|
||||
`package p; func (T) _[ /* ERROR "must have no type parameters" */ A, B C](a A) B`,
|
||||
`package p; func (T) _[ /* ERROR "must have no type parameters" */ A, B C[A, B]](a A) B`,
|
||||
|
||||
`package p; func(*T[e, e /* ERROR "e redeclared" */ ]) _()`,
|
||||
}
|
||||
|
||||
func TestInvalid(t *testing.T) {
|
||||
|
@ -25,10 +25,15 @@ func Add /* =@AddDecl */[T /* =@T */ Addable /* @Addable */](l /* =@l */, r /* =
|
||||
|
||||
type Receiver /* =@Receiver */[P /* =@P */ any] struct {}
|
||||
|
||||
type RP /* =@RP1 */ struct{}
|
||||
|
||||
// TODO(rFindley): make a decision on how/whether to resolve identifiers that
|
||||
// refer to receiver type parameters, as is the case for the 'P' result
|
||||
// parameter below.
|
||||
func (r /* =@recv */ Receiver /* @Receiver */ [P]) m() P {}
|
||||
//
|
||||
// For now, we ensure that types are not incorrectly resolved when receiver
|
||||
// type parameters are in scope.
|
||||
func (r /* =@recv */ Receiver /* @Receiver */ [RP]) m(RP) RP {}
|
||||
|
||||
func f /* =@f */[T1 /* =@T1 */ interface{~[]T2 /* @T2 */}, T2 /* =@T2 */ any](
|
||||
x /* =@x */ T1 /* @T1 */, T1 /* =@T1_duplicate */ y, // Note that this is a bug:
|
||||
@ -41,3 +46,6 @@ func f /* =@f */[T1 /* =@T1 */ interface{~[]T2 /* @T2 */}, T2 /* =@T2 */ any](
|
||||
T1 /* @T1 */ := 0
|
||||
var t1var /* =@t1var */ T1 /* @T1 */
|
||||
}
|
||||
|
||||
// From issue #39634
|
||||
func(*ph1[e, e])h(d)
|
||||
|
2
libgo/go/go/parser/testdata/typeparams.src
vendored
2
libgo/go/go/parser/testdata/typeparams.src
vendored
@ -9,7 +9,7 @@ package p
|
||||
|
||||
type List[E any /* ERROR "expected ']', found any" */ ] []E
|
||||
|
||||
type Pair[L, /* ERROR "expected ']', found ','" */ R any] struct {
|
||||
type Pair[L, /* ERROR "unexpected comma" */ R any] struct {
|
||||
Left L
|
||||
Right R
|
||||
}
|
||||
|
@ -367,20 +367,48 @@ func (p *printer) parameters(fields *ast.FieldList, isTypeParam bool) {
|
||||
p.expr(stripParensAlways(par.Type))
|
||||
prevLine = parLineEnd
|
||||
}
|
||||
|
||||
// if the closing ")" is on a separate line from the last parameter,
|
||||
// print an additional "," and line break
|
||||
if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing {
|
||||
p.print(token.COMMA)
|
||||
p.linebreak(closing, 0, ignore, true)
|
||||
} else if isTypeParam && fields.NumFields() == 1 {
|
||||
// Otherwise, if we are in a type parameter list that could be confused
|
||||
// with the constant array length expression [P*C], print a comma so that
|
||||
// parsing is unambiguous.
|
||||
//
|
||||
// Note that while ParenExprs can also be ambiguous (issue #49482), the
|
||||
// printed type is never parenthesized (stripParensAlways is used above).
|
||||
if t, _ := fields.List[0].Type.(*ast.StarExpr); t != nil && !isTypeLit(t.X) {
|
||||
p.print(token.COMMA)
|
||||
}
|
||||
}
|
||||
|
||||
// unindent if we indented
|
||||
if ws == ignore {
|
||||
p.print(unindent)
|
||||
}
|
||||
}
|
||||
|
||||
p.print(fields.Closing, closeTok)
|
||||
}
|
||||
|
||||
// isTypeLit reports whether x is a (possibly parenthesized) type literal.
|
||||
func isTypeLit(x ast.Expr) bool {
|
||||
switch x := x.(type) {
|
||||
case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
|
||||
return true
|
||||
case *ast.StarExpr:
|
||||
// *T may be a pointer dereferenciation.
|
||||
// Only consider *T as type literal if T is a type literal.
|
||||
return isTypeLit(x.X)
|
||||
case *ast.ParenExpr:
|
||||
return isTypeLit(x.X)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *printer) signature(sig *ast.FuncType) {
|
||||
if sig.TypeParams != nil {
|
||||
p.parameters(sig.TypeParams, true)
|
||||
|
26
libgo/go/go/printer/testdata/generics.golden
vendored
26
libgo/go/go/printer/testdata/generics.golden
vendored
@ -38,3 +38,29 @@ func _() {
|
||||
// type constraint literals with elided interfaces
|
||||
func _[P ~int, Q int | string]() {}
|
||||
func _[P struct{ f int }, Q *P]() {}
|
||||
|
||||
// various potentially ambiguous type parameter lists (issue #49482)
|
||||
type _[P *T,] struct{}
|
||||
type _[P *T, _ any] struct{}
|
||||
type _[P *T,] struct{}
|
||||
type _[P *T, _ any] struct{}
|
||||
type _[P T] struct{}
|
||||
type _[P T, _ any] struct{}
|
||||
|
||||
type _[P *struct{}] struct{}
|
||||
type _[P *struct{}] struct{}
|
||||
type _[P []int] struct{}
|
||||
|
||||
// array type declarations
|
||||
type _ [P(T)]struct{}
|
||||
type _ [P((T))]struct{}
|
||||
type _ [P * *T]struct{}
|
||||
type _ [P * T]struct{}
|
||||
type _ [P(*T)]struct{}
|
||||
type _ [P(**T)]struct{}
|
||||
type _ [P * T]struct{}
|
||||
type _ [P*T - T]struct{}
|
||||
|
||||
type _[
|
||||
P *T,
|
||||
] struct{}
|
||||
|
26
libgo/go/go/printer/testdata/generics.input
vendored
26
libgo/go/go/printer/testdata/generics.input
vendored
@ -35,3 +35,29 @@ func _() {
|
||||
// type constraint literals with elided interfaces
|
||||
func _[P ~int, Q int | string]() {}
|
||||
func _[P struct{f int}, Q *P]() {}
|
||||
|
||||
// various potentially ambiguous type parameter lists (issue #49482)
|
||||
type _[P *T,] struct{}
|
||||
type _[P *T, _ any] struct{}
|
||||
type _[P (*T),] struct{}
|
||||
type _[P (*T), _ any] struct{}
|
||||
type _[P (T),] struct{}
|
||||
type _[P (T), _ any] struct{}
|
||||
|
||||
type _[P *struct{}] struct{}
|
||||
type _[P (*struct{})] struct{}
|
||||
type _[P ([]int)] struct{}
|
||||
|
||||
// array type declarations
|
||||
type _ [P(T)]struct{}
|
||||
type _ [P((T))]struct{}
|
||||
type _ [P * *T]struct{}
|
||||
type _ [P * T]struct{}
|
||||
type _ [P(*T)]struct{}
|
||||
type _ [P(**T)]struct{}
|
||||
type _ [P * T]struct{}
|
||||
type _ [P * T - T]struct{}
|
||||
|
||||
type _[
|
||||
P *T,
|
||||
] struct{}
|
||||
|
@ -419,9 +419,15 @@ func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, i
|
||||
}
|
||||
|
||||
// AssertableTo reports whether a value of type V can be asserted to have type T.
|
||||
// The behavior of AssertableTo is undefined if V is a generalized interface; i.e.,
|
||||
// an interface that may only be used as a type constraint in Go code.
|
||||
func AssertableTo(V *Interface, T Type) bool {
|
||||
m, _ := (*Checker)(nil).assertableTo(V, T)
|
||||
return m == nil
|
||||
// Checker.newAssertableTo suppresses errors for invalid types, so we need special
|
||||
// handling here.
|
||||
if T.Underlying() == Typ[Invalid] {
|
||||
return false
|
||||
}
|
||||
return (*Checker)(nil).newAssertableTo(V, T) == nil
|
||||
}
|
||||
|
||||
// AssignableTo reports whether a value of type V is assignable to a variable of type T.
|
||||
|
@ -2308,27 +2308,27 @@ type Bad Bad // invalid type
|
||||
conf := Config{Error: func(error) {}}
|
||||
pkg, _ := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
|
||||
|
||||
scope := pkg.Scope()
|
||||
lookup := func(tname string) Type { return pkg.Scope().Lookup(tname).Type() }
|
||||
var (
|
||||
EmptyIface = scope.Lookup("EmptyIface").Type().Underlying().(*Interface)
|
||||
I = scope.Lookup("I").Type().(*Named)
|
||||
EmptyIface = lookup("EmptyIface").Underlying().(*Interface)
|
||||
I = lookup("I").(*Named)
|
||||
II = I.Underlying().(*Interface)
|
||||
C = scope.Lookup("C").Type().(*Named)
|
||||
C = lookup("C").(*Named)
|
||||
CI = C.Underlying().(*Interface)
|
||||
Integer = scope.Lookup("Integer").Type().Underlying().(*Interface)
|
||||
EmptyTypeSet = scope.Lookup("EmptyTypeSet").Type().Underlying().(*Interface)
|
||||
N1 = scope.Lookup("N1").Type()
|
||||
Integer = lookup("Integer").Underlying().(*Interface)
|
||||
EmptyTypeSet = lookup("EmptyTypeSet").Underlying().(*Interface)
|
||||
N1 = lookup("N1")
|
||||
N1p = NewPointer(N1)
|
||||
N2 = scope.Lookup("N2").Type()
|
||||
N2 = lookup("N2")
|
||||
N2p = NewPointer(N2)
|
||||
N3 = scope.Lookup("N3").Type()
|
||||
N4 = scope.Lookup("N4").Type()
|
||||
Bad = scope.Lookup("Bad").Type()
|
||||
N3 = lookup("N3")
|
||||
N4 = lookup("N4")
|
||||
Bad = lookup("Bad")
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
t Type
|
||||
i *Interface
|
||||
V Type
|
||||
T *Interface
|
||||
want bool
|
||||
}{
|
||||
{I, II, true},
|
||||
@ -2359,8 +2359,78 @@ type Bad Bad // invalid type
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if got := Implements(test.t, test.i); got != test.want {
|
||||
t.Errorf("Implements(%s, %s) = %t, want %t", test.t, test.i, got, test.want)
|
||||
if got := Implements(test.V, test.T); got != test.want {
|
||||
t.Errorf("Implements(%s, %s) = %t, want %t", test.V, test.T, got, test.want)
|
||||
}
|
||||
|
||||
// The type assertion x.(T) is valid if T is an interface or if T implements the type of x.
|
||||
// The assertion is never valid if T is a bad type.
|
||||
V := test.T
|
||||
T := test.V
|
||||
want := false
|
||||
if _, ok := T.Underlying().(*Interface); (ok || Implements(T, V)) && T != Bad {
|
||||
want = true
|
||||
}
|
||||
if got := AssertableTo(V, T); got != want {
|
||||
t.Errorf("AssertableTo(%s, %s) = %t, want %t", V, T, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMissingMethodAlternative(t *testing.T) {
|
||||
const src = `
|
||||
package p
|
||||
type T interface {
|
||||
m()
|
||||
}
|
||||
|
||||
type V0 struct{}
|
||||
func (V0) m() {}
|
||||
|
||||
type V1 struct{}
|
||||
|
||||
type V2 struct{}
|
||||
func (V2) m() int
|
||||
|
||||
type V3 struct{}
|
||||
func (*V3) m()
|
||||
|
||||
type V4 struct{}
|
||||
func (V4) M()
|
||||
`
|
||||
|
||||
pkg, err := pkgFor("p.go", src, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
T := pkg.Scope().Lookup("T").Type().Underlying().(*Interface)
|
||||
lookup := func(name string) (*Func, bool) {
|
||||
return MissingMethod(pkg.Scope().Lookup(name).Type(), T, true)
|
||||
}
|
||||
|
||||
// V0 has method m with correct signature. Should not report wrongType.
|
||||
method, wrongType := lookup("V0")
|
||||
if method != nil || wrongType {
|
||||
t.Fatalf("V0: got method = %v, wrongType = %v", method, wrongType)
|
||||
}
|
||||
|
||||
checkMissingMethod := func(tname string, reportWrongType bool) {
|
||||
method, wrongType := lookup(tname)
|
||||
if method == nil || method.Name() != "m" || wrongType != reportWrongType {
|
||||
t.Fatalf("%s: got method = %v, wrongType = %v", tname, method, wrongType)
|
||||
}
|
||||
}
|
||||
|
||||
// V1 has no method m. Should not report wrongType.
|
||||
checkMissingMethod("V1", false)
|
||||
|
||||
// V2 has method m with wrong signature type (ignoring receiver). Should report wrongType.
|
||||
checkMissingMethod("V2", true)
|
||||
|
||||
// V3 has no method m but it exists on *V3. Should report wrongType.
|
||||
checkMissingMethod("V3", true)
|
||||
|
||||
// V4 has no method m but has M. Should not report wrongType.
|
||||
checkMissingMethod("V4", false)
|
||||
}
|
||||
|
@ -83,10 +83,24 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
// of S and the respective parameter passing rules apply."
|
||||
S := x.typ
|
||||
var T Type
|
||||
if s, _ := structuralType(S).(*Slice); s != nil {
|
||||
if s, _ := coreType(S).(*Slice); s != nil {
|
||||
T = s.elem
|
||||
} else {
|
||||
check.invalidArg(x, _InvalidAppend, "%s is not a slice", x)
|
||||
var cause string
|
||||
switch {
|
||||
case x.isNil():
|
||||
cause = "have untyped nil"
|
||||
case isTypeParam(S):
|
||||
if u := coreType(S); u != nil {
|
||||
cause = check.sprintf("%s has core type %s", x, u)
|
||||
} else {
|
||||
cause = check.sprintf("%s has no core type", x)
|
||||
}
|
||||
default:
|
||||
cause = check.sprintf("have %s", x)
|
||||
}
|
||||
// don't use Checker.invalidArg here as it would repeat "argument" in the error message
|
||||
check.errorf(x, _InvalidAppend, "first argument to append must be a slice; %s", cause)
|
||||
return
|
||||
}
|
||||
|
||||
@ -102,7 +116,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
if x.mode == invalid {
|
||||
return
|
||||
}
|
||||
if t := structuralString(x.typ); t != nil && isString(t) {
|
||||
if t := coreString(x.typ); t != nil && isString(t) {
|
||||
if check.Types != nil {
|
||||
sig := makeSig(S, S, x.typ)
|
||||
sig.variadic = true
|
||||
@ -143,9 +157,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
// cap(x)
|
||||
// len(x)
|
||||
mode := invalid
|
||||
var typ Type
|
||||
var val constant.Value
|
||||
switch typ = arrayPtrDeref(under(x.typ)); t := typ.(type) {
|
||||
switch t := arrayPtrDeref(under(x.typ)).(type) {
|
||||
case *Basic:
|
||||
if isString(t) && id == _Len {
|
||||
if x.mode == constant_ {
|
||||
@ -202,7 +215,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
}
|
||||
}
|
||||
|
||||
if mode == invalid && typ != Typ[Invalid] {
|
||||
if mode == invalid && under(x.typ) != Typ[Invalid] {
|
||||
code := _InvalidCap
|
||||
if id == _Len {
|
||||
code = _InvalidLen
|
||||
@ -211,12 +224,14 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
return
|
||||
}
|
||||
|
||||
// record the signature before changing x.typ
|
||||
if check.Types != nil && mode != constant_ {
|
||||
check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ))
|
||||
}
|
||||
|
||||
x.mode = mode
|
||||
x.typ = Typ[Int]
|
||||
x.val = val
|
||||
if check.Types != nil && mode != constant_ {
|
||||
check.recordBuiltinType(call.Fun, makeSig(x.typ, typ))
|
||||
}
|
||||
|
||||
case _Close:
|
||||
// close(c)
|
||||
@ -314,7 +329,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
}
|
||||
return nil
|
||||
}
|
||||
resTyp := check.applyTypeFunc(f, x.typ)
|
||||
resTyp := check.applyTypeFunc(f, x, id)
|
||||
if resTyp == nil {
|
||||
check.invalidArg(x, _InvalidComplex, "arguments have type %s, expected floating-point", x.typ)
|
||||
return
|
||||
@ -335,14 +350,14 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
|
||||
case _Copy:
|
||||
// copy(x, y []T) int
|
||||
dst, _ := structuralType(x.typ).(*Slice)
|
||||
dst, _ := coreType(x.typ).(*Slice)
|
||||
|
||||
var y operand
|
||||
arg(&y, 1)
|
||||
if y.mode == invalid {
|
||||
return
|
||||
}
|
||||
src0 := structuralString(y.typ)
|
||||
src0 := coreString(y.typ)
|
||||
if src0 != nil && isString(src0) {
|
||||
src0 = NewSlice(universeByte)
|
||||
}
|
||||
@ -442,7 +457,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
}
|
||||
return nil
|
||||
}
|
||||
resTyp := check.applyTypeFunc(f, x.typ)
|
||||
resTyp := check.applyTypeFunc(f, x, id)
|
||||
if resTyp == nil {
|
||||
code := _InvalidImag
|
||||
if id == _Real {
|
||||
@ -480,13 +495,13 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
}
|
||||
|
||||
var min int // minimum number of arguments
|
||||
switch structuralType(T).(type) {
|
||||
switch coreType(T).(type) {
|
||||
case *Slice:
|
||||
min = 2
|
||||
case *Map, *Chan:
|
||||
min = 1
|
||||
case nil:
|
||||
check.errorf(arg0, _InvalidMake, "cannot make %s: no structural type", arg0)
|
||||
check.errorf(arg0, _InvalidMake, "cannot make %s: no core type", arg0)
|
||||
return
|
||||
default:
|
||||
check.invalidArg(arg0, _InvalidMake, "cannot make %s; type must be slice, map, or channel", arg0)
|
||||
@ -809,8 +824,8 @@ func hasVarSize(t Type) bool {
|
||||
// of x. If any of these applications of f return nil,
|
||||
// applyTypeFunc returns nil.
|
||||
// If x is not a type parameter, the result is f(x).
|
||||
func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
|
||||
if tp, _ := x.(*TypeParam); tp != nil {
|
||||
func (check *Checker) applyTypeFunc(f func(Type) Type, x *operand, id builtinId) Type {
|
||||
if tp, _ := x.typ.(*TypeParam); tp != nil {
|
||||
// Test if t satisfies the requirements for the argument
|
||||
// type and collect possible result types at the same time.
|
||||
var terms []*Term
|
||||
@ -827,17 +842,34 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
|
||||
return nil
|
||||
}
|
||||
|
||||
// We can type-check this fine but we're introducing a synthetic
|
||||
// type parameter for the result. It's not clear what the API
|
||||
// implications are here. Report an error for 1.18 (see #50912),
|
||||
// but continue type-checking.
|
||||
var code errorCode
|
||||
switch id {
|
||||
case _Real:
|
||||
code = _InvalidReal
|
||||
case _Imag:
|
||||
code = _InvalidImag
|
||||
case _Complex:
|
||||
code = _InvalidComplex
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
check.softErrorf(x, code, "%s not supported as argument to %s for go1.18 (see issue #50937)", x, predeclaredFuncs[id].name)
|
||||
|
||||
// Construct a suitable new type parameter for the result type.
|
||||
// The type parameter is placed in the current package so export/import
|
||||
// works as expected.
|
||||
tpar := NewTypeName(token.NoPos, check.pkg, "<type parameter>", nil)
|
||||
tpar := NewTypeName(token.NoPos, check.pkg, tp.obj.name, nil)
|
||||
ptyp := check.newTypeParam(tpar, NewInterfaceType(nil, []Type{NewUnion(terms)})) // assigns type to tpar as a side-effect
|
||||
ptyp.index = tp.index
|
||||
|
||||
return ptyp
|
||||
}
|
||||
|
||||
return f(x)
|
||||
return f(x.typ)
|
||||
}
|
||||
|
||||
// makeSig makes a signature for the given argument and result types.
|
||||
|
@ -29,6 +29,8 @@ var builtinCalls = []struct {
|
||||
{"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant
|
||||
{"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`},
|
||||
{"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`},
|
||||
{"cap", `type S []byte; var s S; _ = cap(s)`, `func(p.S) int`},
|
||||
{"cap", `var s P; _ = cap(s)`, `func(P) int`},
|
||||
|
||||
{"len", `_ = len("foo")`, `invalid type`}, // constant
|
||||
{"len", `var s string; _ = len(s)`, `func(string) int`},
|
||||
@ -37,6 +39,8 @@ var builtinCalls = []struct {
|
||||
{"len", `var s []int64; _ = len(s)`, `func([]int64) int`},
|
||||
{"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`},
|
||||
{"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`},
|
||||
{"len", `type S []byte; var s S; _ = len(s)`, `func(p.S) int`},
|
||||
{"len", `var s P; _ = len(s)`, `func(P) int`},
|
||||
|
||||
{"close", `var c chan int; close(c)`, `func(chan int)`},
|
||||
{"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},
|
||||
@ -159,7 +163,7 @@ func TestBuiltinSignatures(t *testing.T) {
|
||||
func testBuiltinSignature(t *testing.T, name, src0, want string) {
|
||||
t.Skip("skipping for gccgo--no default importer")
|
||||
|
||||
src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _[P any]() { %s }`, src0)
|
||||
src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _[P ~[]byte]() { %s }`, src0)
|
||||
f, err := parser.ParseFile(fset, "", src, 0)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %s", src0, err)
|
||||
|
@ -171,7 +171,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
|
||||
cgocall := x.mode == cgofunc
|
||||
|
||||
// a type parameter may be "called" if all types have the same signature
|
||||
sig, _ := structuralType(x.typ).(*Signature)
|
||||
sig, _ := coreType(x.typ).(*Signature)
|
||||
if sig == nil {
|
||||
check.invalidOp(x, _InvalidCall, "cannot call non-function %s", x)
|
||||
x.mode = invalid
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user