Merge pull request #18571 from containers/renovate/github.com-onsi-ginkgo-v2-2.x

fix(deps): update module github.com/onsi/ginkgo/v2 to v2.9.5
This commit is contained in:
OpenShift Merge Robot 2023-05-16 04:05:36 -04:00 committed by GitHub
commit e2d1ad1fb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 214 additions and 107 deletions

4
go.mod
View File

@ -43,7 +43,7 @@ require (
github.com/mattn/go-sqlite3 v1.14.16
github.com/moby/term v0.5.0
github.com/nxadm/tail v1.4.8
github.com/onsi/ginkgo/v2 v2.9.4
github.com/onsi/ginkgo/v2 v2.9.5
github.com/onsi/gomega v1.27.6
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0-rc3
@ -170,7 +170,7 @@ require (
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/tools v0.8.0 // indirect
golang.org/x/tools v0.9.1 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.55.0 // indirect

8
go.sum
View File

@ -762,8 +762,8 @@ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE=
github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@ -1339,8 +1339,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -4,7 +4,7 @@ go 1.18
require (
github.com/cpuguy83/go-md2man/v2 v2.0.2
github.com/onsi/ginkgo/v2 v2.9.4
github.com/onsi/ginkgo/v2 v2.9.5
github.com/vbatts/git-validation v1.2.0
golang.org/x/tools v0.9.1
)

View File

@ -17,8 +17,8 @@ github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04
github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE=
github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=

View File

@ -1,3 +1,3 @@
package types
const VERSION = "2.9.4"
const VERSION = "2.9.5"

View File

@ -11,7 +11,7 @@ github.com/google/pprof/profile
# github.com/hashicorp/go-version v1.3.0
## explicit
github.com/hashicorp/go-version
# github.com/onsi/ginkgo/v2 v2.9.4
# github.com/onsi/ginkgo/v2 v2.9.5
## explicit; go 1.18
github.com/onsi/ginkgo/v2/config
github.com/onsi/ginkgo/v2/formatter

View File

@ -1,3 +1,14 @@
## 2.9.5
### Fixes
- ensure the correct deterministic sort order is produced when ordered specs are generated by a helper function [7fa0b6b]
### Maintenance
- fix generators link (#1200) [9f9d8b9]
- Bump golang.org/x/tools from 0.8.0 to 0.9.1 (#1196) [150e3f2]
- fix spelling err in docs (#1199) [0013b1a]
- Bump golang.org/x/sys from 0.7.0 to 0.8.0 (#1193) [9e9e3e5]
## 2.9.4
### Fixes

View File

@ -875,6 +875,15 @@ func (n Nodes) FirstNodeMarkedOrdered() Node {
return Node{}
}
func (n Nodes) IndexOfFirstNodeMarkedOrdered() int {
for i := range n {
if n[i].MarkedOrdered {
return i
}
}
return -1
}
func (n Nodes) GetMaxFlakeAttempts() int {
maxFlakeAttempts := 0
for i := range n {

View File

@ -27,36 +27,43 @@ func (s *SortableSpecs) Swap(i, j int) { s.Indexes[i], s.Indexes[j] = s.Indexes[
func (s *SortableSpecs) Less(i, j int) bool {
a, b := s.Specs[s.Indexes[i]], s.Specs[s.Indexes[j]]
firstOrderedA := a.Nodes.FirstNodeMarkedOrdered()
firstOrderedB := b.Nodes.FirstNodeMarkedOrdered()
if firstOrderedA.ID == firstOrderedB.ID && !firstOrderedA.IsZero() {
// strictly preserve order in ordered containers. ID will track this as IDs are generated monotonically
return a.FirstNodeWithType(types.NodeTypeIt).ID < b.FirstNodeWithType(types.NodeTypeIt).ID
aNodes, bNodes := a.Nodes.WithType(types.NodeTypesForContainerAndIt), b.Nodes.WithType(types.NodeTypesForContainerAndIt)
firstOrderedAIdx, firstOrderedBIdx := aNodes.IndexOfFirstNodeMarkedOrdered(), bNodes.IndexOfFirstNodeMarkedOrdered()
if firstOrderedAIdx > -1 && firstOrderedBIdx > -1 && aNodes[firstOrderedAIdx].ID == bNodes[firstOrderedBIdx].ID {
// strictly preserve order within an ordered containers. ID will track this as IDs are generated monotonically
return aNodes.FirstNodeWithType(types.NodeTypeIt).ID < bNodes.FirstNodeWithType(types.NodeTypeIt).ID
}
aCLs := a.Nodes.WithType(types.NodeTypesForContainerAndIt).CodeLocations()
bCLs := b.Nodes.WithType(types.NodeTypesForContainerAndIt).CodeLocations()
for i := 0; i < len(aCLs) && i < len(bCLs); i++ {
aCL, bCL := aCLs[i], bCLs[i]
if aCL.FileName < bCL.FileName {
return true
} else if aCL.FileName > bCL.FileName {
return false
// if either spec is in an ordered container - only use the nodes up to the outermost ordered container
if firstOrderedAIdx > -1 {
aNodes = aNodes[:firstOrderedAIdx+1]
}
if firstOrderedBIdx > -1 {
bNodes = bNodes[:firstOrderedBIdx+1]
}
for i := 0; i < len(aNodes) && i < len(bNodes); i++ {
aCL, bCL := aNodes[i].CodeLocation, bNodes[i].CodeLocation
if aCL.FileName != bCL.FileName {
return aCL.FileName < bCL.FileName
}
if aCL.LineNumber < bCL.LineNumber {
return true
} else if aCL.LineNumber > bCL.LineNumber {
return false
if aCL.LineNumber != bCL.LineNumber {
return aCL.LineNumber < bCL.LineNumber
}
}
// either everything is equal or we have different lengths of CLs
if len(aCLs) < len(bCLs) {
return true
} else if len(aCLs) > len(bCLs) {
return false
if len(aNodes) != len(bNodes) {
return len(aNodes) < len(bNodes)
}
// ok, now we are sure everything was equal. so we use the spec text to break ties
return a.Text() < b.Text()
for i := 0; i < len(aNodes); i++ {
if aNodes[i].Text != bNodes[i].Text {
return aNodes[i].Text < bNodes[i].Text
}
}
// ok, all those texts were equal. we'll use the ID of the most deeply nested node as a last resort
return aNodes[len(aNodes)-1].ID < bNodes[len(bNodes)-1].ID
}
type GroupedSpecIndices []SpecIndices

View File

@ -1,3 +1,3 @@
package types
const VERSION = "2.9.4"
const VERSION = "2.9.5"

View File

@ -113,6 +113,20 @@ const (
opObj = 'O' // .Obj() (Named, TypeParam)
)
// For is equivalent to new(Encoder).For(obj).
//
// It may be more efficient to reuse a single Encoder across several calls.
func For(obj types.Object) (Path, error) {
return new(Encoder).For(obj)
}
// An Encoder amortizes the cost of encoding the paths of multiple objects.
// The zero value of an Encoder is ready to use.
type Encoder struct {
scopeNamesMemo map[*types.Scope][]string // memoization of Scope.Names()
namedMethodsMemo map[*types.Named][]*types.Func // memoization of namedMethods()
}
// For returns the path to an object relative to its package,
// or an error if the object is not accessible from the package's Scope.
//
@ -145,24 +159,7 @@ const (
// .Type().Field(0) (field Var X)
//
// where p is the package (*types.Package) to which X belongs.
func For(obj types.Object) (Path, error) {
return newEncoderFor()(obj)
}
// An encoder amortizes the cost of encoding the paths of multiple objects.
// Nonexported pending approval of proposal 58668.
type encoder struct {
scopeNamesMemo map[*types.Scope][]string // memoization of Scope.Names()
namedMethodsMemo map[*types.Named][]*types.Func // memoization of namedMethods()
}
// Exposed to gopls via golang.org/x/tools/internal/typesinternal
// pending approval of proposal 58668.
//
//go:linkname newEncoderFor
func newEncoderFor() func(types.Object) (Path, error) { return new(encoder).For }
func (enc *encoder) For(obj types.Object) (Path, error) {
func (enc *Encoder) For(obj types.Object) (Path, error) {
pkg := obj.Pkg()
// This table lists the cases of interest.
@ -341,7 +338,7 @@ func appendOpArg(path []byte, op byte, arg int) []byte {
// This function is just an optimization that avoids the general scope walking
// approach. You are expected to fall back to the general approach if this
// function fails.
func (enc *encoder) concreteMethod(meth *types.Func) (Path, bool) {
func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
// Concrete methods can only be declared on package-scoped named types. For
// that reason we can skip the expensive walk over the package scope: the
// path will always be package -> named type -> method. We can trivially get
@ -421,7 +418,13 @@ func (enc *encoder) concreteMethod(meth *types.Func) (Path, bool) {
}
}
panic(fmt.Sprintf("couldn't find method %s on type %s", meth, named))
// Due to golang/go#59944, go/types fails to associate the receiver with
// certain methods on cgo types.
//
// TODO(rfindley): replace this panic once golang/go#59944 is fixed in all Go
// versions gopls supports.
return "", false
// panic(fmt.Sprintf("couldn't find method %s on type %s; methods: %#v", meth, named, enc.namedMethods(named)))
}
// find finds obj within type T, returning the path to it, or nil if not found.
@ -730,8 +733,23 @@ func namedMethods(named *types.Named) []*types.Func {
return methods
}
// namedMethods is a memoization of the namedMethods function. Callers must not modify the result.
func (enc *Encoder) namedMethods(named *types.Named) []*types.Func {
m := enc.namedMethodsMemo
if m == nil {
m = make(map[*types.Named][]*types.Func)
enc.namedMethodsMemo = m
}
methods, ok := m[named]
if !ok {
methods = namedMethods(named) // allocates and sorts
m[named] = methods
}
return methods
}
// scopeNames is a memoization of scope.Names. Callers must not modify the result.
func (enc *encoder) scopeNames(scope *types.Scope) []string {
func (enc *Encoder) scopeNames(scope *types.Scope) []string {
m := enc.scopeNamesMemo
if m == nil {
m = make(map[*types.Scope][]string)
@ -744,19 +762,3 @@ func (enc *encoder) scopeNames(scope *types.Scope) []string {
}
return names
}
// namedMethods is a memoization of the namedMethods function. Callers must not modify the result.
func (enc *encoder) namedMethods(named *types.Named) []*types.Func {
m := enc.namedMethodsMemo
if m == nil {
m = make(map[*types.Named][]*types.Func)
enc.namedMethodsMemo = m
}
methods, ok := m[named]
if !ok {
methods = namedMethods(named) // allocates and sorts
m[named] = methods
}
return methods
}

View File

@ -8,10 +8,12 @@ package gocommand
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"log"
"os"
"reflect"
"regexp"
"runtime"
"strconv"
@ -215,6 +217,18 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
cmd := exec.Command("go", goArgs...)
cmd.Stdout = stdout
cmd.Stderr = stderr
// cmd.WaitDelay was added only in go1.20 (see #50436).
if waitDelay := reflect.ValueOf(cmd).Elem().FieldByName("WaitDelay"); waitDelay.IsValid() {
// https://go.dev/issue/59541: don't wait forever copying stderr
// after the command has exited.
// After CL 484741 we copy stdout manually, so we we'll stop reading that as
// soon as ctx is done. However, we also don't want to wait around forever
// for stderr. Give a much-longer-than-reasonable delay and then assume that
// something has wedged in the kernel or runtime.
waitDelay.Set(reflect.ValueOf(30 * time.Second))
}
// On darwin the cwd gets resolved to the real path, which breaks anything that
// expects the working directory to keep the original path, including the
// go command when dealing with modules.
@ -229,6 +243,7 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir)
cmd.Dir = i.WorkingDir
}
defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
return runCmdContext(ctx, cmd)
@ -242,10 +257,85 @@ var DebugHangingGoCommands = false
// runCmdContext is like exec.CommandContext except it sends os.Interrupt
// before os.Kill.
func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
if err := cmd.Start(); err != nil {
func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
// If cmd.Stdout is not an *os.File, the exec package will create a pipe and
// copy it to the Writer in a goroutine until the process has finished and
// either the pipe reaches EOF or command's WaitDelay expires.
//
// However, the output from 'go list' can be quite large, and we don't want to
// keep reading (and allocating buffers) if we've already decided we don't
// care about the output. We don't want to wait for the process to finish, and
// we don't wait to wait for the WaitDelay to expire either.
//
// Instead, if cmd.Stdout requires a copying goroutine we explicitly replace
// it with a pipe (which is an *os.File), which we can close in order to stop
// copying output as soon as we realize we don't care about it.
var stdoutW *os.File
if cmd.Stdout != nil {
if _, ok := cmd.Stdout.(*os.File); !ok {
var stdoutR *os.File
stdoutR, stdoutW, err = os.Pipe()
if err != nil {
return err
}
prevStdout := cmd.Stdout
cmd.Stdout = stdoutW
stdoutErr := make(chan error, 1)
go func() {
_, err := io.Copy(prevStdout, stdoutR)
if err != nil {
err = fmt.Errorf("copying stdout: %w", err)
}
stdoutErr <- err
}()
defer func() {
// We started a goroutine to copy a stdout pipe.
// Wait for it to finish, or terminate it if need be.
var err2 error
select {
case err2 = <-stdoutErr:
stdoutR.Close()
case <-ctx.Done():
stdoutR.Close()
// Per https://pkg.go.dev/os#File.Close, the call to stdoutR.Close
// should cause the Read call in io.Copy to unblock and return
// immediately, but we still need to receive from stdoutErr to confirm
// that that has happened.
<-stdoutErr
err2 = ctx.Err()
}
if err == nil {
err = err2
}
}()
// Per https://pkg.go.dev/os/exec#Cmd, “If Stdout and Stderr are the
// same writer, and have a type that can be compared with ==, at most
// one goroutine at a time will call Write.”
//
// Since we're starting a goroutine that writes to cmd.Stdout, we must
// also update cmd.Stderr so that that still holds.
func() {
defer func() { recover() }()
if cmd.Stderr == prevStdout {
cmd.Stderr = cmd.Stdout
}
}()
}
}
err = cmd.Start()
if stdoutW != nil {
// The child process has inherited the pipe file,
// so close the copy held in this process.
stdoutW.Close()
stdoutW = nil
}
if err != nil {
return err
}
resChan := make(chan error, 1)
go func() {
resChan <- cmd.Wait()
@ -253,11 +343,14 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
// If we're interested in debugging hanging Go commands, stop waiting after a
// minute and panic with interesting information.
if DebugHangingGoCommands {
debug := DebugHangingGoCommands
if debug {
timer := time.NewTimer(1 * time.Minute)
defer timer.Stop()
select {
case err := <-resChan:
return err
case <-time.After(1 * time.Minute):
case <-timer.C:
HandleHangingGoCommand(cmd.Process)
case <-ctx.Done():
}
@ -270,30 +363,25 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
}
// Cancelled. Interrupt and see if it ends voluntarily.
cmd.Process.Signal(os.Interrupt)
select {
case err := <-resChan:
return err
case <-time.After(time.Second):
if err := cmd.Process.Signal(os.Interrupt); err == nil {
// (We used to wait only 1s but this proved
// fragile on loaded builder machines.)
timer := time.NewTimer(5 * time.Second)
defer timer.Stop()
select {
case err := <-resChan:
return err
case <-timer.C:
}
}
// Didn't shut down in response to interrupt. Kill it hard.
// TODO(rfindley): per advice from bcmills@, it may be better to send SIGQUIT
// on certain platforms, such as unix.
if err := cmd.Process.Kill(); err != nil && DebugHangingGoCommands {
// Don't panic here as this reliably fails on windows with EINVAL.
if err := cmd.Process.Kill(); err != nil && !errors.Is(err, os.ErrProcessDone) && debug {
log.Printf("error killing the Go command: %v", err)
}
// See above: don't wait indefinitely if we're debugging hanging Go commands.
if DebugHangingGoCommands {
select {
case err := <-resChan:
return err
case <-time.After(10 * time.Second): // a shorter wait as resChan should return quickly following Kill
HandleHangingGoCommand(cmd.Process)
}
}
return <-resChan
}

View File

@ -23,21 +23,11 @@ import (
func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) {
inv.Verb = "list"
inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`}
inv.Env = append(append([]string{}, inv.Env...), "GO111MODULE=off")
// Unset any unneeded flags, and remove them from BuildFlags, if they're
// present.
inv.ModFile = ""
inv.BuildFlags = nil // This is not a build command.
inv.ModFlag = ""
var buildFlags []string
for _, flag := range inv.BuildFlags {
// Flags can be prefixed by one or two dashes.
f := strings.TrimPrefix(strings.TrimPrefix(flag, "-"), "-")
if strings.HasPrefix(f, "mod=") || strings.HasPrefix(f, "modfile=") {
continue
}
buildFlags = append(buildFlags, flag)
}
inv.BuildFlags = buildFlags
inv.ModFile = ""
inv.Env = append(inv.Env[:len(inv.Env):len(inv.Env)], "GO111MODULE=off")
stdoutBytes, err := r.Run(ctx, inv)
if err != nil {
return 0, err

4
vendor/modules.txt vendored
View File

@ -678,7 +678,7 @@ github.com/nxadm/tail/winfile
# github.com/oklog/ulid v1.3.1
## explicit
github.com/oklog/ulid
# github.com/onsi/ginkgo/v2 v2.9.4
# github.com/onsi/ginkgo/v2 v2.9.5
## explicit; go 1.18
github.com/onsi/ginkgo/v2
github.com/onsi/ginkgo/v2/config
@ -1012,7 +1012,7 @@ golang.org/x/text/secure/bidirule
golang.org/x/text/transform
golang.org/x/text/unicode/bidi
golang.org/x/text/unicode/norm
# golang.org/x/tools v0.8.0
# golang.org/x/tools v0.9.1
## explicit; go 1.18
golang.org/x/tools/cmd/stringer
golang.org/x/tools/go/ast/inspector