[dev.simd] all: merge master (773701a) into dev.simd

Merge List:

+ 2025-06-10 773701a853 internal/trace: pass GOTRACEBACK=crash to testprogs
+ 2025-06-10 fb0c27c514 os: do not follow dangling symlinks in Root when O_CREATE|O_EXCL on AIX
+ 2025-06-10 1cafdfb63b net/http: make the zero value of CrossOriginProtection work
+ 2025-06-10 a35701b352 cmd/dist: only install necessary tools when doing local test
+ 2025-06-10 a189516d3a runtime: don't do a direct G handoff in semrelease on systemstack
+ 2025-06-10 f18d046568 all.{bash,rc}: use "../bin/go tool dist" instead of "%GOTOOLDIR%/dist" print build info
+ 2025-06-09 ee7bfbdbcc cmd/compile/internal/ssa: fix PPC64 merging of (AND (S[RL]Dconst ...)
+ 2025-06-09 985d600f3a runtime: use small struct TestSynctest to ensure cleanups run
+ 2025-06-09 848a768ba7 runtime: clarify stack traces for bubbled goroutines
+ 2025-06-09 049a5e6036 runtime: return a different bubble deadlock error when main goroutine is done
+ 2025-06-09 ac1686752b cmd/internal/doc: increase version of pkgsite doc command that's run
+ 2025-06-09 da0e8c4517 cmd/compile: relax reshaping condition
+ 2025-06-09 7800f4f0ad log/slog: fix level doc on handlers
+ 2025-06-07 d184f8dc02 runtime: check for gsignal in racecall on loong64
+ 2025-06-06 0ccfbc834a os/signal: doc link to syscall.EPIPE
+ 2025-06-06 78eadf5b3d all: update vendored dependencies [generated]
+ 2025-06-05 4d1c255f15 net/http: strip sensitive proxy headers from redirect requests
+ 2025-06-04 3432c68467 runtime: make bubbled timers more consistent with unbubbled
+ 2025-06-04 1aa3362093 Revert "cmd/compile: Enable inlining of tail calls"
+ 2025-06-04 f537061e1b cmd/trace: handle Sync event at the beginning of the trace
+ 2025-06-04 d4bf716793 runtime: reduce per-P memory footprint when greenteagc is disabled
+ 2025-06-04 1f2a4d192d test: add another regression test for issue 73309
+ 2025-06-04 5b748eed9c cmd/compile: better error message when import embed package
+ 2025-06-03 cfb4e9bc4a cmd/dist: don't install tools that won't be shipped in distribution
+ 2025-06-03 94764d0938 cmd/doc: build cmd/doc directly into the go command
+ 2025-06-03 74b70eead7 go/token: remove unreachable code
+ 2025-06-03 0c0094c893 go/token: tweak comment
+ 2025-06-03 792548a483 cmd/go/internal/cfg: fix GOROOT setting when forcing host config
+ 2025-06-02 49f6304724 runtime: additional memmove benchmarks
+ 2025-06-02 eebae283b6 go/token: FileSet: hold Files in a balanced tree
+ 2025-06-02 3bd0eab96f runtime: randomize order of timers at the same instant in bubbles
+ 2025-06-02 a379698521 go/{ast,parser,types}: add signpost to golang.org/x/tools/go/packages
+ 2025-06-02 497cb7c0c3 cmd/compile/internal/noder: document quirk of string elements
+ 2025-06-02 cc119ee391 cmd/compile/internal/noder: stub type section and adjust others
+ 2025-06-02 25ca686a0b cmd/compile/internal/noder: begin filling in SectionObj
+ 2025-06-02 11660d537b cmd/compile/internal/noder: fill in SectionName

Change-Id: I7c0a7c56105f1a6912f4ed122d615d12b1ea7877
This commit is contained in:
Cherry Mui 2025-06-10 14:57:15 -04:00
commit b2e8ddba3c
129 changed files with 20141 additions and 18479 deletions

View file

@ -10,4 +10,4 @@ if [ ! -f make.bash ]; then
fi
. ./make.bash "$@" --no-banner
bash run.bash --no-rebuild
"$GOTOOLDIR/dist" banner # print build info
../bin/go tool dist banner # print build info

View file

@ -13,4 +13,4 @@ if(! test -f make.rc){
. ./make.rc --no-banner $*
bind -b $GOROOT/bin /bin
./run.rc --no-rebuild
$GOTOOLDIR/dist banner # print build info
../bin/go tool dist banner # print build info

View file

@ -785,7 +785,7 @@ func inlineCallCheck(callerfn *ir.Func, call *ir.CallExpr) (bool, bool) {
if call.Op() != ir.OCALLFUNC {
return false, false
}
if call.GoDefer {
if call.GoDefer || call.NoInline {
return false, false
}

View file

@ -279,7 +279,12 @@ func (s *inlClosureState) mark(n ir.Node) ir.Node {
ok := match(n)
// can't wrap TailCall's child into ParenExpr
if t, ok := n.(*ir.TailCallStmt); ok {
ir.EditChildren(t.Call, s.mark)
} else {
ir.EditChildren(n, s.mark)
}
if ok {
if p == nil {
@ -317,23 +322,6 @@ func (s *inlClosureState) unparenthesize() {
n = paren.X
}
ir.EditChildren(n, unparen)
// special case for tail calls: if the tail call was inlined, transform
// the tail call to a return stmt if the inlined function was not void,
// otherwise replace it with the inlined expression followed by a return.
if tail, ok := n.(*ir.TailCallStmt); ok {
if inl, done := tail.Call.(*ir.InlinedCallExpr); done {
if len(inl.ReturnVars) != 0 {
ret := ir.NewReturnStmt(tail.Pos(), []ir.Node{inl})
if len(inl.ReturnVars) > 1 {
typecheck.RewriteMultiValueCall(ret, inl)
}
n = ret
} else {
ret := ir.NewReturnStmt(tail.Pos(), nil)
n = ir.NewBlockStmt(tail.Pos(), []ir.Node{inl, ret})
}
}
}
return n
}
ir.EditChildren(s.fn, unparen)
@ -370,9 +358,11 @@ func (s *inlClosureState) fixpoint() bool {
}
func match(n ir.Node) bool {
switch n.(type) {
switch n := n.(type) {
case *ir.CallExpr:
return true
case *ir.TailCallStmt:
n.Call.NoInline = true // can't inline yet
}
return false
}

View file

@ -191,6 +191,7 @@ type CallExpr struct {
KeepAlive []*Name // vars to be kept alive until call returns
IsDDD bool
GoDefer bool // whether this call is part of a go or defer statement
NoInline bool // whether this call must not be inlined
}
func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr {

View file

@ -2202,13 +2202,13 @@ func (n *TailCallStmt) doChildrenWithHidden(do func(Node) bool) bool {
func (n *TailCallStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
if n.Call != nil {
n.Call = edit(n.Call)
n.Call = edit(n.Call).(*CallExpr)
}
}
func (n *TailCallStmt) editChildrenWithHidden(edit func(Node) Node) {
editNodes(n.init, edit)
if n.Call != nil {
n.Call = edit(n.Call)
n.Call = edit(n.Call).(*CallExpr)
}
}

View file

@ -479,7 +479,7 @@ func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt {
// code generation to jump directly to another function entirely.
type TailCallStmt struct {
miniStmt
Call Node // the underlying call
Call *CallExpr // the underlying call
}
func NewTailCallStmt(pos src.XPos, call *CallExpr) *TailCallStmt {

View file

@ -25,9 +25,9 @@ determines its index in the series.
SectionMeta
SectionPosBase
SectionPkg
SectionName // TODO(markfreeman) Define.
SectionType // TODO(markfreeman) Define.
SectionObj // TODO(markfreeman) Define.
SectionName
SectionType
SectionObj
SectionObjExt // TODO(markfreeman) Define.
SectionObjDict // TODO(markfreeman) Define.
SectionBody // TODO(markfreeman) Define.
@ -35,9 +35,11 @@ determines its index in the series.
# Sections
A section is a series of elements of a type determined by the section's
kind. Go constructs are mapped onto (potentially multiple) elements.
Elements are accessed using an index relative to the start of the
section.
kind. Go constructs are mapped onto one or more elements with possibly
different types; in that case, the elements are in different sections.
Elements are accessed using an element index relative to the start of
the section.
RelElemIdx = Uint64 .
@ -47,6 +49,9 @@ outside the string section access string values by reference.
SectionString = { String } .
Note that despite being an element, a string does not begin with a
reference table.
## Meta Section
The meta section provides fundamental information for a package. It
contains exactly two elements a public root and a private root.
@ -135,6 +140,97 @@ Note, a PkgRef is *not* equivalent to Ref[Pkg] due to an extra marker.
Ref[Pkg]
.
## Type Section
The type section is a series of type definition elements.
SectionType = { TypeDef } .
A type definition can be in one of several formats, which are identified
by their TypeSpec code.
TypeDef = RefTable
[ Sync ]
[ Sync ]
Uint64 // denotes which TypeSpec to use
TypeSpec
.
TypeSpec = TypeSpecBasic // TODO(markfreeman): Define.
| TypeSpecNamed // TODO(markfreeman): Define.
| TypeSpecPointer // TODO(markfreeman): Define.
| TypeSpecSlice // TODO(markfreeman): Define.
| TypeSpecArray // TODO(markfreeman): Define.
| TypeSpecChan // TODO(markfreeman): Define.
| TypeSpecMap // TODO(markfreeman): Define.
| TypeSpecSignature // TODO(markfreeman): Define.
| TypeSpecStruct // TODO(markfreeman): Define.
| TypeSpecInterface // TODO(markfreeman): Define.
| TypeSpecUnion // TODO(markfreeman): Define.
| TypeSpecTypeParam // TODO(markfreeman): Define.
.
// TODO(markfreeman): Document the reader dictionary once we understand it more.
To use a type elsewhere, a TypeUse is encoded.
TypeUse = [ Sync ]
Bool // whether it is a derived type
[ Uint64 ] // if derived, an index into the reader dictionary
[ Ref[TypeDef] ] // else, a reference to the type
.
## Object Sections
Information about an object (e.g. variable, function, type name, etc.)
is split into multiple elements in different sections. Those elements
have the same section-relative element index.
### Name Section
The name section holds a series of names.
SectionName = { Name } .
Names are elements holding qualified identifiers and type information
for objects.
Name = RefTable
[ Sync ]
[ Sync ]
PkgRef // the object's package
StringRef // the object's package-local name
[ Sync ]
Uint64 // the object's type (e.g. Var, Func, etc.)
.
### Definition Section
The definition section holds definitions for objects defined by the target
package; it does not contain definitions for imported objects.
SectionObj = { ObjectDef } .
Object definitions can be in one of several formats. To determine the correct
format, the name section must be referenced; it contains a code indicating
the object's type.
ObjectDef = RefTable
[ Sync ]
ObjectSpec
.
ObjectSpec = ObjectSpecConst // TODO(markfreeman) Define.
| ObjectSpecFunc // TODO(markfreeman) Define.
| ObjectSpecAlias // TODO(markfreeman) Define.
| ObjectSpecNamedType // TODO(markfreeman) Define.
| ObjectSpecVar // TODO(markfreeman) Define.
.
To use an object definition elsewhere, an ObjectUse is encoded.
ObjectUse = [ Sync ]
[ Bool ]
Ref[ObjectDef]
Uint64 // the number of type arguments
{ TypeUse } // references to the type arguments
.
# References
A reference table precedes every element. Each entry in the table
contains a (section, index) pair denoting the location of the
@ -152,9 +248,14 @@ referenced element.
Elements encode references to other elements as an index in the
reference table not the location of the referenced element directly.
// TODO(markfreeman): Rename to RefUse.
UseReloc = [ Sync ]
RelElemIdx
RefTableIdx = Uint64 .
To do this, the Ref[T] primitive is used as below; note that this is
the same shape as provided by package pkgbits, just with new
interpretation applied.
Ref[T] = [ Sync ]
RefTableIdx // the Uint64
.
# Primitives

View file

@ -458,7 +458,7 @@ func Renameinit() *types.Sym {
func checkEmbed(decl *syntax.VarDecl, haveEmbed, withinFunc bool) error {
switch {
case !haveEmbed:
return errors.New("go:embed only allowed in Go files that import \"embed\"")
return errors.New("go:embed requires import \"embed\" (or import _ \"embed\", if package is not used)")
case len(decl.NameList) > 1:
return errors.New("go:embed cannot apply to multiple vars")
case decl.Values != nil:

View file

@ -1014,7 +1014,25 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx index, implicits, explicits
// arguments.
for i, targ := range dict.targs {
basic := r.Bool()
if dict.shaped && !pr.reshaping {
isPointerShape := basic && targ.IsPtr() && !targ.Elem().NotInHeap()
// We should not do shapify during the reshaping process, see #71184.
// However, this only matters for shapify a pointer type, which will
// lose the original underlying type.
//
// Example with a pointer type:
//
// - First, shapifying *[]T -> *uint8
// - During the reshaping process, *uint8 is shapified to *go.shape.uint8
// - This ends up with a different type with the original *[]T
//
// For a non-pointer type:
//
// - int -> go.shape.int
// - go.shape.int -> go.shape.int
//
// We always end up with the identical type.
canShapify := !pr.reshaping || !isPointerShape
if dict.shaped && canShapify {
dict.targs[i] = shapify(targ, basic)
}
}
@ -3996,11 +4014,12 @@ func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) {
if recv.Type() != nil && recv.Type().IsPtr() && method.Type.Recv().Type.IsPtr() &&
method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) &&
!unifiedHaveInlineBody(ir.MethodExprName(dot).Func) &&
!(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) {
if base.Debug.TailCall != 0 {
base.WarnfAt(fn.Nname.Type().Recv().Type.Elem().Pos(), "tail call emitted for the method %v wrapper", method.Nname)
}
// Prefer OTAILCALL to reduce code size (the called method can be inlined).
// Prefer OTAILCALL to reduce code size (except the case when the called method can be inlined).
fn.Body.Append(ir.NewTailCallStmt(pos, call))
return
}

View file

@ -1583,6 +1583,11 @@ func GetPPC64Shiftme(auxint int64) int64 {
// operation. Masks can also extend from the msb and wrap to
// the lsb too. That is, the valid masks are 32 bit strings
// of the form: 0..01..10..0 or 1..10..01..1 or 1...1
//
// Note: This ignores the upper 32 bits of the input. When a
// zero extended result is desired (e.g a 64 bit result), the
// user must verify the upper 32 bits are 0 and the mask is
// contiguous (that is, non-wrapping).
func isPPC64WordRotateMask(v64 int64) bool {
// Isolate rightmost 1 (if none 0) and add.
v := uint32(v64)
@ -1593,6 +1598,16 @@ func isPPC64WordRotateMask(v64 int64) bool {
return (v&vp == 0 || vn&vpn == 0) && v != 0
}
// Test if this mask is a valid, contiguous bitmask which can be
// represented by a RLWNM mask and also clears the upper 32 bits
// of the register.
func isPPC64WordRotateMaskNonWrapping(v64 int64) bool {
// Isolate rightmost 1 (if none 0) and add.
v := uint32(v64)
vp := (v & -v) + v
return (v&vp == 0) && v != 0 && uint64(uint32(v64)) == uint64(v64)
}
// Compress mask and shift into single value of the form
// me | mb<<8 | rotate<<16 | nbits<<24 where me and mb can
// be used to regenerate the input mask.
@ -1702,7 +1717,7 @@ func mergePPC64AndSrdi(m, s int64) int64 {
if rv&uint64(mask) != 0 {
return 0
}
if !isPPC64WordRotateMask(mask) {
if !isPPC64WordRotateMaskNonWrapping(mask) {
return 0
}
return encodePPC64RotateMask((32-s)&31, mask, 32)
@ -1717,7 +1732,7 @@ func mergePPC64AndSldi(m, s int64) int64 {
if rv&uint64(mask) != 0 {
return 0
}
if !isPPC64WordRotateMask(mask) {
if !isPPC64WordRotateMaskNonWrapping(mask) {
return 0
}
return encodePPC64RotateMask(s&31, mask, 32)

View file

@ -1924,7 +1924,7 @@ func (s *state) stmt(n ir.Node) {
case ir.OTAILCALL:
n := n.(*ir.TailCallStmt)
s.callResult(n.Call.(*ir.CallExpr), callTail)
s.callResult(n.Call, callTail)
call := s.mem()
b := s.endBlock()
b.Kind = ssa.BlockRetJmp // could use BlockExit. BlockRetJmp is mostly for clarity.

View file

@ -137,7 +137,7 @@ assignOK:
if cr > len(rhs) {
stmt := stmt.(*ir.AssignListStmt)
stmt.SetOp(ir.OAS2FUNC)
r := rhs[0]
r := rhs[0].(*ir.CallExpr)
rtyp := r.Type()
mismatched := false

View file

@ -333,6 +333,7 @@ func TestStdFixed(t *testing.T) {
"issue56103.go", // anonymous interface cycles; will be a type checker error in 1.22
"issue52697.go", // types2 does not have constraints on stack size
"issue73309.go", // this test requires GODEBUG=gotypesalias=1
"issue73309b.go", // this test requires GODEBUG=gotypesalias=1
// These tests requires runtime/cgo.Incomplete, which is only available on some platforms.
// However, types2 does not know about build constraints.

View file

@ -139,8 +139,7 @@ func walkStmt(n ir.Node) ir.Node {
n := n.(*ir.TailCallStmt)
var init ir.Nodes
call := n.Call.(*ir.CallExpr)
call.Fun = walkExpr(call.Fun, &init)
n.Call.Fun = walkExpr(n.Call.Fun, &init)
if len(init) > 0 {
init.Append(n)

View file

@ -0,0 +1,125 @@
go build main.go
! stdout .
! stderr .
-- main.go --
package main
import (
"p/b"
)
func main() {
f()
}
func f() {
typ := indexedPageType{newIndexedType(nil)}
page := newPage(typ.indexedType)
page.Data()
}
func newPage(typ *indexedType) Page {
values := typ.NewValues(nil, nil)
return &indexedPage{
typ: typ,
values: values.Int32(),
columnIndex: ^0,
}
}
type Type interface {
NewPage(columnIndex, numValues int, data b.Values) Page
NewValues(values []byte, offsets []uint32) b.Values
}
type Page interface {
Type() Type
Data() b.Values
}
type indexedPage struct {
typ *indexedType
values []int32
columnIndex int16
}
func (page *indexedPage) Type() Type { return indexedPageType{page.typ} }
func (page *indexedPage) Data() b.Values { return b.Int32Values(page.values) }
type indexedType struct {
Type
}
func newIndexedType(typ Type) *indexedType {
return &indexedType{Type: typ}
}
type indexedPageType struct{ *indexedType }
func (t indexedPageType) NewValues(values []byte, _ []uint32) b.Values {
return b.Int32ValuesFromBytes(values)
}
-- go.mod --
module p
go 1.24
-- internal/a/a.go --
package a
import "unsafe"
type slice struct {
ptr unsafe.Pointer
len int
cap int
}
func Slice[To, From any](data []From) []To {
// This function could use unsafe.Slice but it would drop the capacity
// information, so instead we implement the type conversion.
var zf From
var zt To
var s = slice{
ptr: unsafe.Pointer(unsafe.SliceData(data)),
len: int((uintptr(len(data)) * unsafe.Sizeof(zf)) / unsafe.Sizeof(zt)),
cap: int((uintptr(cap(data)) * unsafe.Sizeof(zf)) / unsafe.Sizeof(zt)),
}
return *(*[]To)(unsafe.Pointer(&s))
}
-- b/b.go --
package b
import "p/internal/a"
type Kind int32
const Int32 Kind = iota + 2
type Values struct {
kind Kind
size int32
data []byte
offsets []uint32
}
func (v *Values) Int32() []int32 {
return a.Slice[int32](v.data)
}
func makeValues[T any](kind Kind, values []T) Values {
return Values{kind: kind, data: a.Slice[byte](values)}
}
func Int32Values(values []int32) Values {
return makeValues(Int32, values)
}
func Int32ValuesFromBytes(values []byte) Values {
return Values{kind: Int32, data: values}
}

37
src/cmd/dist/build.go vendored
View file

@ -1390,7 +1390,21 @@ func toolenv() []string {
return env
}
var toolchain = []string{"cmd/asm", "cmd/cgo", "cmd/compile", "cmd/link", "cmd/preprofile"}
var (
toolchain = []string{"cmd/asm", "cmd/cgo", "cmd/compile", "cmd/link", "cmd/preprofile"}
// Keep in sync with binExes in cmd/distpack/pack.go.
binExesIncludedInDistpack = []string{"cmd/go", "cmd/gofmt"}
// Keep in sync with the filter in cmd/distpack/pack.go.
toolsIncludedInDistpack = []string{"cmd/asm", "cmd/cgo", "cmd/compile", "cmd/cover", "cmd/link", "cmd/preprofile", "cmd/vet"}
// We could install all tools in "cmd", but is unnecessary because we will
// remove them in distpack, so instead install the tools that will actually
// be included in distpack, which is a superset of toolchain. Not installing
// the tools will help us test what happens when the tools aren't present.
toolsToInstall = slices.Concat(binExesIncludedInDistpack, toolsIncludedInDistpack)
)
// The bootstrap command runs a build from scratch,
// stopping at having installed the go_bootstrap command.
@ -1456,11 +1470,6 @@ func cmdbootstrap() {
// GOEXPERIMENT.
os.Setenv("GOEXPERIMENT", "none")
if debug {
// cmd/buildid is used in debug mode.
toolchain = append(toolchain, "cmd/buildid")
}
if isdir(pathf("%s/src/pkg", goroot)) {
fatalf("\n\n"+
"The Go package sources have moved to $GOROOT/src.\n"+
@ -1516,7 +1525,7 @@ func cmdbootstrap() {
}
// To recap, so far we have built the new toolchain
// (cmd/asm, cmd/cgo, cmd/compile, cmd/link)
// (cmd/asm, cmd/cgo, cmd/compile, cmd/link, cmd/preprofile)
// using the Go bootstrap toolchain and go command.
// Then we built the new go command (as go_bootstrap)
// using the new toolchain and our own build logic (above).
@ -1605,9 +1614,9 @@ func cmdbootstrap() {
xprintf("\n")
}
xprintf("Building commands for host, %s/%s.\n", goos, goarch)
goInstall(toolenv(), goBootstrap, "cmd")
checkNotStale(toolenv(), goBootstrap, "cmd")
checkNotStale(toolenv(), gorootBinGo, "cmd")
goInstall(toolenv(), goBootstrap, toolsToInstall...)
checkNotStale(toolenv(), goBootstrap, toolsToInstall...)
checkNotStale(toolenv(), gorootBinGo, toolsToInstall...)
timelog("build", "target toolchain")
if vflag > 0 {
@ -1621,12 +1630,12 @@ func cmdbootstrap() {
xprintf("Building packages and commands for target, %s/%s.\n", goos, goarch)
}
goInstall(nil, goBootstrap, "std")
goInstall(toolenv(), goBootstrap, "cmd")
goInstall(toolenv(), goBootstrap, toolsToInstall...)
checkNotStale(toolenv(), goBootstrap, toolchain...)
checkNotStale(nil, goBootstrap, "std")
checkNotStale(toolenv(), goBootstrap, "cmd")
checkNotStale(toolenv(), goBootstrap, toolsToInstall...)
checkNotStale(nil, gorootBinGo, "std")
checkNotStale(toolenv(), gorootBinGo, "cmd")
checkNotStale(toolenv(), gorootBinGo, toolsToInstall...)
if debug {
run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
checkNotStale(toolenv(), goBootstrap, toolchain...)
@ -1677,7 +1686,7 @@ func cmdbootstrap() {
if distpack {
xprintf("Packaging archives for %s/%s.\n", goos, goarch)
run("", ShowOutput|CheckExit, pathf("%s/distpack", tooldir))
run("", ShowOutput|CheckExit, gorootBinGo, "tool", "distpack")
}
// Print trailing banner unless instructed otherwise.

View file

@ -178,7 +178,7 @@ func (t *tester) run() {
// otherwise relevant to the actual set of packages under test.
goInstall(toolenv(), gorootBinGo, toolchain...)
goInstall(toolenv(), gorootBinGo, toolchain...)
goInstall(toolenv(), gorootBinGo, "cmd")
goInstall(toolenv(), gorootBinGo, toolsToInstall...)
}
}

View file

@ -171,6 +171,7 @@ func main() {
switch strings.TrimSuffix(path.Base(name), ".exe") {
default:
return false
// Keep in sync with toolsIncludedInDistpack in cmd/dist/build.go.
case "asm", "cgo", "compile", "cover", "link", "preprofile", "vet":
}
}
@ -179,6 +180,7 @@ func main() {
// Add go and gofmt to bin, using cross-compiled binaries
// if this is a cross-compiled distribution.
// Keep in sync with binExesIncludedInDistpack in cmd/dist/build.go.
binExes := []string{
"go",
"gofmt",

55
src/cmd/doc/doc.go Normal file
View file

@ -0,0 +1,55 @@
// Copyright 2015 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.
// Doc (usually run as go doc) accepts zero, one or two arguments.
//
// Zero arguments:
//
// go doc
//
// Show the documentation for the package in the current directory.
//
// One argument:
//
// go doc <pkg>
// go doc <sym>[.<methodOrField>]
// go doc [<pkg>.]<sym>[.<methodOrField>]
// go doc [<pkg>.][<sym>.]<methodOrField>
//
// The first item in this list that succeeds is the one whose documentation
// is printed. If there is a symbol but no package, the package in the current
// directory is chosen. However, if the argument begins with a capital
// letter it is always assumed to be a symbol in the current directory.
//
// Two arguments:
//
// go doc <pkg> <sym>[.<methodOrField>]
//
// Show the documentation for the package, symbol, and method or field. The
// first argument must be a full package path. This is similar to the
// command-line usage for the godoc command.
//
// For commands, unless the -cmd flag is present "go doc command"
// shows only the package-level docs for the package.
//
// The -src flag causes doc to print the full source code for the symbol, such
// as the body of a struct, function or method.
//
// The -all flag causes doc to print all documentation for the package and
// all its visible symbols. The argument must identify a package.
//
// For complete documentation, run "go help doc".
package main
import (
"cmd/internal/doc"
"cmd/internal/telemetry/counter"
"os"
)
func main() {
counter.Open()
counter.Inc("doc/invocations")
doc.Main(os.Args[1:])
}

View file

@ -4,18 +4,18 @@ go 1.25
require (
github.com/google/pprof v0.0.0-20250208200701-d0013a598941
golang.org/x/arch v0.14.0
golang.org/x/build v0.0.0-20250211223606-a5e3f75caa63
golang.org/x/mod v0.24.1-0.20250508140430-9d3333156f46
golang.org/x/sync v0.13.0
golang.org/x/sys v0.32.0
golang.org/x/telemetry v0.0.0-20250212145848-75305293b65a
golang.org/x/term v0.29.0
golang.org/x/tools v0.32.1-0.20250423190156-68e94bd1775e
golang.org/x/arch v0.18.1-0.20250605182141-b2f4e2807dec
golang.org/x/build v0.0.0-20250606033421-8c8ff6f34a83
golang.org/x/mod v0.25.0
golang.org/x/sync v0.15.0
golang.org/x/sys v0.33.0
golang.org/x/telemetry v0.0.0-20250606142133-60998feb31a8
golang.org/x/term v0.32.0
golang.org/x/tools v0.34.0
)
require (
github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/text v0.26.0 // indirect
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef // indirect
)

View file

@ -6,23 +6,23 @@ github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd h1:EVX1s+X
github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4=
golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/build v0.0.0-20250211223606-a5e3f75caa63 h1:QZ8/V1B4oK7N5t6w0zX5dAxFIHt0WaTX+r1z29cWXjY=
golang.org/x/build v0.0.0-20250211223606-a5e3f75caa63/go.mod h1:JhINjMoWj8G2oLkaBLNDBIr/GLqJNOkCr4XzFWWYCf4=
golang.org/x/mod v0.24.1-0.20250508140430-9d3333156f46 h1:0wufKs7434dECGChJ8f683kuQsBh+1MXieCdOlBOBw8=
golang.org/x/mod v0.24.1-0.20250508140430-9d3333156f46/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20250212145848-75305293b65a h1:3fgycqG+90xOafOruMBVZXa8DUeOt5qbGLjQoNvZ8Ew=
golang.org/x/telemetry v0.0.0-20250212145848-75305293b65a/go.mod h1:Ng+6E7PnWNge4EifZkPKeQUnm5iyAoH8qQgw3pLCiF4=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/tools v0.32.1-0.20250423190156-68e94bd1775e h1:t16U5lZsxU6d2R2ax2lfujEzzsctlYTvXppAlsAWP4A=
golang.org/x/tools v0.32.1-0.20250423190156-68e94bd1775e/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
golang.org/x/arch v0.18.1-0.20250605182141-b2f4e2807dec h1:fCOjXc18tBlkVy4m+VuL1WU8VTukYOGtAk7nC5QYPRY=
golang.org/x/arch v0.18.1-0.20250605182141-b2f4e2807dec/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/build v0.0.0-20250606033421-8c8ff6f34a83 h1:IiFSc399rOkpudtnsTDKdtfFEsvd+dGfNfl+ytV267c=
golang.org/x/build v0.0.0-20250606033421-8c8ff6f34a83/go.mod h1:SDzKvZFXqZyl3tLink1AnKsAocWm0yFc3UfmxR6aIOw=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20250606142133-60998feb31a8 h1:jBJ3nsFeGb1DzjhOg2ZgZTpnDnOZfHId7RNlBJUtkOM=
golang.org/x/telemetry v0.0.0-20250606142133-60998feb31a8/go.mod h1:mUcjA5g0luJpMYCLjhH91f4t4RAUNp+zq9ZmUoqPD7M=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef h1:mqLYrXCXYEZOop9/Dbo6RPX11539nwiCNBb1icVPmw8=
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ=

View file

@ -1093,10 +1093,10 @@ func TestGoListTest(t *testing.T) {
tg.grepStdoutNot(`^testing \[bytes.test\]$`, "unexpected test copy of testing")
tg.grepStdoutNot(`^testing$`, "unexpected real copy of testing")
tg.run("list", "-test", "cmd/buildid", "cmd/doc")
tg.run("list", "-test", "cmd/buildid", "cmd/gofmt")
tg.grepStdout(`^cmd/buildid$`, "missing cmd/buildid")
tg.grepStdout(`^cmd/doc$`, "missing cmd/doc")
tg.grepStdout(`^cmd/doc\.test$`, "missing cmd/doc test")
tg.grepStdout(`^cmd/gofmt$`, "missing cmd/gofmt")
tg.grepStdout(`^cmd/gofmt\.test$`, "missing cmd/gofmt test")
tg.grepStdoutNot(`^cmd/buildid\.test$`, "unexpected cmd/buildid test")
tg.grepStdoutNot(`^testing`, "unexpected testing")

View file

@ -227,6 +227,8 @@ func ForceHost() {
// Recompute the build context using Goos and Goarch to
// set the correct value for ctx.CgoEnabled.
BuildContext = defaultContext()
// Call SetGOROOT to properly set the GOROOT on the new context.
SetGOROOT(Getenv("GOROOT"), false)
// Recompute experiments: the settings determined depend on GOOS and GOARCH.
// This will also update the BuildContext's tool tags to include the new
// experiment tags.

View file

@ -2,17 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !cmd_go_bootstrap
// Package doc implements the “go doc” command.
package doc
import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/internal/doc"
"context"
"errors"
"os"
"os/exec"
"path/filepath"
)
var CmdDoc = &base.Command{
@ -134,13 +132,5 @@ Flags:
}
func runDoc(ctx context.Context, cmd *base.Command, args []string) {
base.StartSigHandlers()
err := base.RunErr(cfg.BuildToolexec, filepath.Join(cfg.GOROOTbin, "go"), "tool", "doc", args)
if err != nil {
var ee *exec.ExitError
if errors.As(err, &ee) {
os.Exit(ee.ExitCode())
}
base.Error(err)
}
doc.Main(args)
}

View file

@ -0,0 +1,13 @@
// Copyright 2025 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.
//go:build cmd_go_bootstrap
// Don't build cmd/doc into go_bootstrap because it depends on net.
package doc
import "cmd/go/internal/base"
var CmdDoc = &base.Command{}

View file

@ -56,7 +56,7 @@ stdout '"Action":"fail","Package":"m/cycle/p","Elapsed":.*,"FailedBuild":"m/cycl
! go test -json -o=$devnull ./veterror
stdout '"ImportPath":"m/veterror \[m/veterror.test\]","Action":"build-output","Output":"# m/veterror\\n"'
stdout '"ImportPath":"m/veterror \[m/veterror.test\]","Action":"build-output","Output":"# \[m/veterror\]\\n"'
stdout '"ImportPath":"m/veterror \[m/veterror.test\]","Action":"build-output","Output":"veterror(/|\\\\)main_test.go:9:9: fmt.Printf format %s reads arg #1, but call has 0 args\\n"'
stdout '"ImportPath":"m/veterror \[m/veterror.test\]","Action":"build-output","Output":"veterror(/|\\\\)main_test.go:9:21: fmt.Printf format %s reads arg #1, but call has 0 args\\n"'
stdout '"ImportPath":"m/veterror \[m/veterror.test\]","Action":"build-fail"'
stdout '"Action":"start","Package":"m/veterror"'
stdout '"Action":"output","Package":"m/veterror","Output":"FAIL\\tm/veterror \[build failed\]\\n"'

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
package doc
import (
"bytes"

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
package doc
import (
"bytes"
@ -90,7 +90,7 @@ type test struct {
no []string // Regular expressions that should not match.
}
const p = "cmd/doc/testdata"
const p = "cmd/internal/doc/testdata"
var tests = []test{
// Sanity check.
@ -105,7 +105,7 @@ var tests = []test{
{
"package clause",
[]string{p},
[]string{`package pkg.*cmd/doc/testdata`},
[]string{`package pkg.*cmd/internal/doc/testdata`},
nil,
},

View file

@ -2,45 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Doc (usually run as go doc) accepts zero, one or two arguments.
//
// Zero arguments:
//
// go doc
//
// Show the documentation for the package in the current directory.
//
// One argument:
//
// go doc <pkg>
// go doc <sym>[.<methodOrField>]
// go doc [<pkg>.]<sym>[.<methodOrField>]
// go doc [<pkg>.][<sym>.]<methodOrField>
//
// The first item in this list that succeeds is the one whose documentation
// is printed. If there is a symbol but no package, the package in the current
// directory is chosen. However, if the argument begins with a capital
// letter it is always assumed to be a symbol in the current directory.
//
// Two arguments:
//
// go doc <pkg> <sym>[.<methodOrField>]
//
// Show the documentation for the package, symbol, and method or field. The
// first argument must be a full package path. This is similar to the
// command-line usage for the godoc command.
//
// For commands, unless the -cmd flag is present "go doc command"
// shows only the package-level docs for the package.
//
// The -src flag causes doc to print the full source code for the symbol, such
// as the body of a struct, function or method.
//
// The -all flag causes doc to print all documentation for the package and
// all its visible symbols. The argument must identify a package.
//
// For complete documentation, run "go help doc".
package main
// Package doc provides the implementation of the "go doc" subcommand and cmd/doc.
package doc
import (
"bytes"
@ -74,7 +37,7 @@ var (
)
// usage is a replacement usage function for the flags package.
func usage() {
func usage(flagSet *flag.FlagSet) {
fmt.Fprintf(os.Stderr, "Usage of [go] doc:\n")
fmt.Fprintf(os.Stderr, "\tgo doc\n")
fmt.Fprintf(os.Stderr, "\tgo doc <pkg>\n")
@ -85,16 +48,17 @@ func usage() {
fmt.Fprintf(os.Stderr, "For more information run\n")
fmt.Fprintf(os.Stderr, "\tgo help doc\n\n")
fmt.Fprintf(os.Stderr, "Flags:\n")
flag.PrintDefaults()
flagSet.PrintDefaults()
os.Exit(2)
}
func main() {
// Main is the entry point, invoked both by go doc and cmd/doc.
func Main(args []string) {
log.SetFlags(0)
log.SetPrefix("doc: ")
counter.Open()
dirsInit()
err := do(os.Stdout, flag.CommandLine, os.Args[1:])
var flagSet flag.FlagSet
err := do(os.Stdout, &flagSet, args)
if err != nil {
log.Fatal(err)
}
@ -102,7 +66,7 @@ func main() {
// do is the workhorse, broken out of main to make testing easier.
func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
flagSet.Usage = usage
flagSet.Usage = func() { usage(flagSet) }
unexported = false
matchCase = false
flagSet.StringVar(&chdir, "C", "", "change to `dir` before running command")
@ -114,7 +78,6 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
flagSet.BoolVar(&short, "short", false, "one-line representation for each symbol")
flagSet.BoolVar(&serveHTTP, "http", false, "serve HTML docs over HTTP")
flagSet.Parse(args)
counter.Inc("doc/invocations")
counter.CountFlags("doc/flag:", *flag.CommandLine)
if chdir != "" {
if err := os.Chdir(chdir); err != nil {
@ -151,7 +114,7 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
// Loop until something is printed.
dirs.Reset()
for i := 0; ; i++ {
buildPackage, userPath, sym, more := parseArgs(flagSet.Args())
buildPackage, userPath, sym, more := parseArgs(flagSet, flagSet.Args())
if i > 0 && !more { // Ignore the "more" bit on the first iteration.
return failMessage(paths, symbol, method)
}
@ -165,7 +128,7 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
unexported = true
}
symbol, method = parseSymbol(sym)
symbol, method = parseSymbol(flagSet, sym)
pkg := parsePackage(writer, buildPackage, userPath)
paths = append(paths, pkg.prettyPath())
@ -268,7 +231,7 @@ func doPkgsite(urlPath string) error {
env = append(env, "GOPROXY="+goproxy)
}
const version = "v0.0.0-20250520201116-40659211760d"
const version = "v0.0.0-20250608123103-82c52f1754cd"
cmd := exec.Command("go", "run", "golang.org/x/pkgsite/cmd/internal/doc@"+version,
"-gorepo", buildCtx.GOROOT,
"-http", addr,
@ -338,7 +301,7 @@ func failMessage(paths []string, symbol, method string) error {
// and there may be more matches. For example, if the argument
// is rand.Float64, we must scan both crypto/rand and math/rand
// to find the symbol, and the first call will return crypto/rand, true.
func parseArgs(args []string) (pkg *build.Package, path, symbol string, more bool) {
func parseArgs(flagSet *flag.FlagSet, args []string) (pkg *build.Package, path, symbol string, more bool) {
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
@ -356,7 +319,7 @@ func parseArgs(args []string) (pkg *build.Package, path, symbol string, more boo
}
switch len(args) {
default:
usage()
usage(flagSet)
case 1:
// Done below.
case 2:
@ -499,7 +462,7 @@ func importDir(dir string) *build.Package {
// parseSymbol breaks str apart into a symbol and method.
// Both may be missing or the method may be missing.
// If present, each must be a valid Go identifier.
func parseSymbol(str string) (symbol, method string) {
func parseSymbol(flagSet *flag.FlagSet, str string) (symbol, method string) {
if str == "" {
return
}
@ -510,7 +473,7 @@ func parseSymbol(str string) (symbol, method string) {
method = elem[1]
default:
log.Printf("too many periods in symbol specification")
usage()
usage(flagSet)
}
symbol = elem[0]
return

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
package doc
import (
"bufio"

View file

@ -4,7 +4,7 @@
//go:build plan9 || windows
package main
package doc
import (
"os"

View file

@ -4,7 +4,7 @@
//go:build unix || js || wasip1
package main
package doc
import (
"os"

View file

@ -215,12 +215,12 @@ func (g *stackSampleGenerator[R]) StackSample(ctx *traceContext, ev *trace.Event
// to trace.ResourceNone (the global scope).
type globalRangeGenerator struct {
ranges map[string]activeRange
seenSync bool
seenSync int
}
// Sync notifies the generator of an EventSync event.
func (g *globalRangeGenerator) Sync() {
g.seenSync = true
g.seenSync++
}
// GlobalRange implements a handler for EventRange* events whose Scope.Kind is ResourceNone.
@ -234,8 +234,9 @@ func (g *globalRangeGenerator) GlobalRange(ctx *traceContext, ev *trace.Event) {
case trace.EventRangeBegin:
g.ranges[r.Name] = activeRange{ev.Time(), ev.Stack()}
case trace.EventRangeActive:
// If we've seen a Sync event, then Active events are always redundant.
if !g.seenSync {
// If we've seen at least 2 Sync events (indicating that we're in at least the second
// generation), then Active events are always redundant.
if g.seenSync < 2 {
// Otherwise, they extend back to the start of the trace.
g.ranges[r.Name] = activeRange{ctx.startTime, ev.Stack()}
}
@ -294,12 +295,12 @@ func (g *globalMetricGenerator) GlobalMetric(ctx *traceContext, ev *trace.Event)
// ResourceProc.
type procRangeGenerator struct {
ranges map[trace.Range]activeRange
seenSync bool
seenSync int
}
// Sync notifies the generator of an EventSync event.
func (g *procRangeGenerator) Sync() {
g.seenSync = true
g.seenSync++
}
// ProcRange implements a handler for EventRange* events whose Scope.Kind is ResourceProc.
@ -313,8 +314,9 @@ func (g *procRangeGenerator) ProcRange(ctx *traceContext, ev *trace.Event) {
case trace.EventRangeBegin:
g.ranges[r] = activeRange{ev.Time(), ev.Stack()}
case trace.EventRangeActive:
// If we've seen a Sync event, then Active events are always redundant.
if !g.seenSync {
// If we've seen at least 2 Sync events (indicating that we're in at least the second
// generation), then Active events are always redundant.
if g.seenSync < 2 {
// Otherwise, they extend back to the start of the trace.
g.ranges[r] = activeRange{ctx.startTime, ev.Stack()}
}

View file

@ -469,7 +469,7 @@ func (rea RegExtshiftAmount) String() string {
if rea.amount != 0 {
buf += fmt.Sprintf(" #%d", rea.amount)
} else {
if rea.show_zero == true {
if rea.show_zero {
buf += fmt.Sprintf(" #%d", rea.amount)
}
}
@ -527,7 +527,7 @@ func (m MemImmediate) String() string {
postR := post.String()
return fmt.Sprintf("[%s], %s", R, postR)
}
return fmt.Sprintf("unimplemented!")
return "unimplemented!"
}
// A MemExtend is a memory reference made up of a base R and index expression X.
@ -1021,110 +1021,110 @@ func (s sysInstFields) getType() sys {
}
var sysInstsAttrs = map[sysInstFields]sysInstAttrs{
sysInstFields{0, 8, 3, 0}: {sys_TLBI, "VMALLE1IS", false},
sysInstFields{0, 8, 3, 1}: {sys_TLBI, "VAE1IS", true},
sysInstFields{0, 8, 3, 2}: {sys_TLBI, "ASIDE1IS", true},
sysInstFields{0, 8, 3, 3}: {sys_TLBI, "VAAE1IS", true},
sysInstFields{0, 8, 3, 5}: {sys_TLBI, "VALE1IS", true},
sysInstFields{0, 8, 3, 7}: {sys_TLBI, "VAALE1IS", true},
sysInstFields{0, 8, 7, 0}: {sys_TLBI, "VMALLE1", false},
sysInstFields{0, 8, 7, 1}: {sys_TLBI, "VAE1", true},
sysInstFields{0, 8, 7, 2}: {sys_TLBI, "ASIDE1", true},
sysInstFields{0, 8, 7, 3}: {sys_TLBI, "VAAE1", true},
sysInstFields{0, 8, 7, 5}: {sys_TLBI, "VALE1", true},
sysInstFields{0, 8, 7, 7}: {sys_TLBI, "VAALE1", true},
sysInstFields{4, 8, 0, 1}: {sys_TLBI, "IPAS2E1IS", true},
sysInstFields{4, 8, 0, 5}: {sys_TLBI, "IPAS2LE1IS", true},
sysInstFields{4, 8, 3, 0}: {sys_TLBI, "ALLE2IS", false},
sysInstFields{4, 8, 3, 1}: {sys_TLBI, "VAE2IS", true},
sysInstFields{4, 8, 3, 4}: {sys_TLBI, "ALLE1IS", false},
sysInstFields{4, 8, 3, 5}: {sys_TLBI, "VALE2IS", true},
sysInstFields{4, 8, 3, 6}: {sys_TLBI, "VMALLS12E1IS", false},
sysInstFields{4, 8, 4, 1}: {sys_TLBI, "IPAS2E1", true},
sysInstFields{4, 8, 4, 5}: {sys_TLBI, "IPAS2LE1", true},
sysInstFields{4, 8, 7, 0}: {sys_TLBI, "ALLE2", false},
sysInstFields{4, 8, 7, 1}: {sys_TLBI, "VAE2", true},
sysInstFields{4, 8, 7, 4}: {sys_TLBI, "ALLE1", false},
sysInstFields{4, 8, 7, 5}: {sys_TLBI, "VALE2", true},
sysInstFields{4, 8, 7, 6}: {sys_TLBI, "VMALLS12E1", false},
sysInstFields{6, 8, 3, 0}: {sys_TLBI, "ALLE3IS", false},
sysInstFields{6, 8, 3, 1}: {sys_TLBI, "VAE3IS", true},
sysInstFields{6, 8, 3, 5}: {sys_TLBI, "VALE3IS", true},
sysInstFields{6, 8, 7, 0}: {sys_TLBI, "ALLE3", false},
sysInstFields{6, 8, 7, 1}: {sys_TLBI, "VAE3", true},
sysInstFields{6, 8, 7, 5}: {sys_TLBI, "VALE3", true},
sysInstFields{0, 8, 1, 0}: {sys_TLBI, "VMALLE1OS", false},
sysInstFields{0, 8, 1, 1}: {sys_TLBI, "VAE1OS", true},
sysInstFields{0, 8, 1, 2}: {sys_TLBI, "ASIDE1OS", true},
sysInstFields{0, 8, 1, 3}: {sys_TLBI, "VAAE1OS", true},
sysInstFields{0, 8, 1, 5}: {sys_TLBI, "VALE1OS", true},
sysInstFields{0, 8, 1, 7}: {sys_TLBI, "VAALE1OS", true},
sysInstFields{0, 8, 2, 1}: {sys_TLBI, "RVAE1IS", true},
sysInstFields{0, 8, 2, 3}: {sys_TLBI, "RVAAE1IS", true},
sysInstFields{0, 8, 2, 5}: {sys_TLBI, "RVALE1IS", true},
sysInstFields{0, 8, 2, 7}: {sys_TLBI, "RVAALE1IS", true},
sysInstFields{0, 8, 5, 1}: {sys_TLBI, "RVAE1OS", true},
sysInstFields{0, 8, 5, 3}: {sys_TLBI, "RVAAE1OS", true},
sysInstFields{0, 8, 5, 5}: {sys_TLBI, "RVALE1OS", true},
sysInstFields{0, 8, 5, 7}: {sys_TLBI, "RVAALE1OS", true},
sysInstFields{0, 8, 6, 1}: {sys_TLBI, "RVAE1", true},
sysInstFields{0, 8, 6, 3}: {sys_TLBI, "RVAAE1", true},
sysInstFields{0, 8, 6, 5}: {sys_TLBI, "RVALE1", true},
sysInstFields{0, 8, 6, 7}: {sys_TLBI, "RVAALE1", true},
sysInstFields{4, 8, 0, 2}: {sys_TLBI, "RIPAS2E1IS", true},
sysInstFields{4, 8, 0, 6}: {sys_TLBI, "RIPAS2LE1IS", true},
sysInstFields{4, 8, 1, 0}: {sys_TLBI, "ALLE2OS", false},
sysInstFields{4, 8, 1, 1}: {sys_TLBI, "VAE2OS", true},
sysInstFields{4, 8, 1, 4}: {sys_TLBI, "ALLE1OS", false},
sysInstFields{4, 8, 1, 5}: {sys_TLBI, "VALE2OS", true},
sysInstFields{4, 8, 1, 6}: {sys_TLBI, "VMALLS12E1OS", false},
sysInstFields{4, 8, 2, 1}: {sys_TLBI, "RVAE2IS", true},
sysInstFields{4, 8, 2, 5}: {sys_TLBI, "RVALE2IS", true},
sysInstFields{4, 8, 4, 0}: {sys_TLBI, "IPAS2E1OS", true},
sysInstFields{4, 8, 4, 2}: {sys_TLBI, "RIPAS2E1", true},
sysInstFields{4, 8, 4, 3}: {sys_TLBI, "RIPAS2E1OS", true},
sysInstFields{4, 8, 4, 4}: {sys_TLBI, "IPAS2LE1OS", true},
sysInstFields{4, 8, 4, 6}: {sys_TLBI, "RIPAS2LE1", true},
sysInstFields{4, 8, 4, 7}: {sys_TLBI, "RIPAS2LE1OS", true},
sysInstFields{4, 8, 5, 1}: {sys_TLBI, "RVAE2OS", true},
sysInstFields{4, 8, 5, 5}: {sys_TLBI, "RVALE2OS", true},
sysInstFields{4, 8, 6, 1}: {sys_TLBI, "RVAE2", true},
sysInstFields{4, 8, 6, 5}: {sys_TLBI, "RVALE2", true},
sysInstFields{6, 8, 1, 0}: {sys_TLBI, "ALLE3OS", false},
sysInstFields{6, 8, 1, 1}: {sys_TLBI, "VAE3OS", true},
sysInstFields{6, 8, 1, 5}: {sys_TLBI, "VALE3OS", true},
sysInstFields{6, 8, 2, 1}: {sys_TLBI, "RVAE3IS", true},
sysInstFields{6, 8, 2, 5}: {sys_TLBI, "RVALE3IS", true},
sysInstFields{6, 8, 5, 1}: {sys_TLBI, "RVAE3OS", true},
sysInstFields{6, 8, 5, 5}: {sys_TLBI, "RVALE3OS", true},
sysInstFields{6, 8, 6, 1}: {sys_TLBI, "RVAE3", true},
sysInstFields{6, 8, 6, 5}: {sys_TLBI, "RVALE3", true},
sysInstFields{0, 7, 6, 1}: {sys_DC, "IVAC", true},
sysInstFields{0, 7, 6, 2}: {sys_DC, "ISW", true},
sysInstFields{0, 7, 10, 2}: {sys_DC, "CSW", true},
sysInstFields{0, 7, 14, 2}: {sys_DC, "CISW", true},
sysInstFields{3, 7, 4, 1}: {sys_DC, "ZVA", true},
sysInstFields{3, 7, 10, 1}: {sys_DC, "CVAC", true},
sysInstFields{3, 7, 11, 1}: {sys_DC, "CVAU", true},
sysInstFields{3, 7, 14, 1}: {sys_DC, "CIVAC", true},
sysInstFields{0, 7, 6, 3}: {sys_DC, "IGVAC", true},
sysInstFields{0, 7, 6, 4}: {sys_DC, "IGSW", true},
sysInstFields{0, 7, 6, 5}: {sys_DC, "IGDVAC", true},
sysInstFields{0, 7, 6, 6}: {sys_DC, "IGDSW", true},
sysInstFields{0, 7, 10, 4}: {sys_DC, "CGSW", true},
sysInstFields{0, 7, 10, 6}: {sys_DC, "CGDSW", true},
sysInstFields{0, 7, 14, 4}: {sys_DC, "CIGSW", true},
sysInstFields{0, 7, 14, 6}: {sys_DC, "CIGDSW", true},
sysInstFields{3, 7, 4, 3}: {sys_DC, "GVA", true},
sysInstFields{3, 7, 4, 4}: {sys_DC, "GZVA", true},
sysInstFields{3, 7, 10, 3}: {sys_DC, "CGVAC", true},
sysInstFields{3, 7, 10, 5}: {sys_DC, "CGDVAC", true},
sysInstFields{3, 7, 12, 3}: {sys_DC, "CGVAP", true},
sysInstFields{3, 7, 12, 5}: {sys_DC, "CGDVAP", true},
sysInstFields{3, 7, 13, 3}: {sys_DC, "CGVADP", true},
sysInstFields{3, 7, 13, 5}: {sys_DC, "CGDVADP", true},
sysInstFields{3, 7, 14, 3}: {sys_DC, "CIGVAC", true},
sysInstFields{3, 7, 14, 5}: {sys_DC, "CIGDVAC", true},
sysInstFields{3, 7, 12, 1}: {sys_DC, "CVAP", true},
sysInstFields{3, 7, 13, 1}: {sys_DC, "CVADP", true},
{0, 8, 3, 0}: {sys_TLBI, "VMALLE1IS", false},
{0, 8, 3, 1}: {sys_TLBI, "VAE1IS", true},
{0, 8, 3, 2}: {sys_TLBI, "ASIDE1IS", true},
{0, 8, 3, 3}: {sys_TLBI, "VAAE1IS", true},
{0, 8, 3, 5}: {sys_TLBI, "VALE1IS", true},
{0, 8, 3, 7}: {sys_TLBI, "VAALE1IS", true},
{0, 8, 7, 0}: {sys_TLBI, "VMALLE1", false},
{0, 8, 7, 1}: {sys_TLBI, "VAE1", true},
{0, 8, 7, 2}: {sys_TLBI, "ASIDE1", true},
{0, 8, 7, 3}: {sys_TLBI, "VAAE1", true},
{0, 8, 7, 5}: {sys_TLBI, "VALE1", true},
{0, 8, 7, 7}: {sys_TLBI, "VAALE1", true},
{4, 8, 0, 1}: {sys_TLBI, "IPAS2E1IS", true},
{4, 8, 0, 5}: {sys_TLBI, "IPAS2LE1IS", true},
{4, 8, 3, 0}: {sys_TLBI, "ALLE2IS", false},
{4, 8, 3, 1}: {sys_TLBI, "VAE2IS", true},
{4, 8, 3, 4}: {sys_TLBI, "ALLE1IS", false},
{4, 8, 3, 5}: {sys_TLBI, "VALE2IS", true},
{4, 8, 3, 6}: {sys_TLBI, "VMALLS12E1IS", false},
{4, 8, 4, 1}: {sys_TLBI, "IPAS2E1", true},
{4, 8, 4, 5}: {sys_TLBI, "IPAS2LE1", true},
{4, 8, 7, 0}: {sys_TLBI, "ALLE2", false},
{4, 8, 7, 1}: {sys_TLBI, "VAE2", true},
{4, 8, 7, 4}: {sys_TLBI, "ALLE1", false},
{4, 8, 7, 5}: {sys_TLBI, "VALE2", true},
{4, 8, 7, 6}: {sys_TLBI, "VMALLS12E1", false},
{6, 8, 3, 0}: {sys_TLBI, "ALLE3IS", false},
{6, 8, 3, 1}: {sys_TLBI, "VAE3IS", true},
{6, 8, 3, 5}: {sys_TLBI, "VALE3IS", true},
{6, 8, 7, 0}: {sys_TLBI, "ALLE3", false},
{6, 8, 7, 1}: {sys_TLBI, "VAE3", true},
{6, 8, 7, 5}: {sys_TLBI, "VALE3", true},
{0, 8, 1, 0}: {sys_TLBI, "VMALLE1OS", false},
{0, 8, 1, 1}: {sys_TLBI, "VAE1OS", true},
{0, 8, 1, 2}: {sys_TLBI, "ASIDE1OS", true},
{0, 8, 1, 3}: {sys_TLBI, "VAAE1OS", true},
{0, 8, 1, 5}: {sys_TLBI, "VALE1OS", true},
{0, 8, 1, 7}: {sys_TLBI, "VAALE1OS", true},
{0, 8, 2, 1}: {sys_TLBI, "RVAE1IS", true},
{0, 8, 2, 3}: {sys_TLBI, "RVAAE1IS", true},
{0, 8, 2, 5}: {sys_TLBI, "RVALE1IS", true},
{0, 8, 2, 7}: {sys_TLBI, "RVAALE1IS", true},
{0, 8, 5, 1}: {sys_TLBI, "RVAE1OS", true},
{0, 8, 5, 3}: {sys_TLBI, "RVAAE1OS", true},
{0, 8, 5, 5}: {sys_TLBI, "RVALE1OS", true},
{0, 8, 5, 7}: {sys_TLBI, "RVAALE1OS", true},
{0, 8, 6, 1}: {sys_TLBI, "RVAE1", true},
{0, 8, 6, 3}: {sys_TLBI, "RVAAE1", true},
{0, 8, 6, 5}: {sys_TLBI, "RVALE1", true},
{0, 8, 6, 7}: {sys_TLBI, "RVAALE1", true},
{4, 8, 0, 2}: {sys_TLBI, "RIPAS2E1IS", true},
{4, 8, 0, 6}: {sys_TLBI, "RIPAS2LE1IS", true},
{4, 8, 1, 0}: {sys_TLBI, "ALLE2OS", false},
{4, 8, 1, 1}: {sys_TLBI, "VAE2OS", true},
{4, 8, 1, 4}: {sys_TLBI, "ALLE1OS", false},
{4, 8, 1, 5}: {sys_TLBI, "VALE2OS", true},
{4, 8, 1, 6}: {sys_TLBI, "VMALLS12E1OS", false},
{4, 8, 2, 1}: {sys_TLBI, "RVAE2IS", true},
{4, 8, 2, 5}: {sys_TLBI, "RVALE2IS", true},
{4, 8, 4, 0}: {sys_TLBI, "IPAS2E1OS", true},
{4, 8, 4, 2}: {sys_TLBI, "RIPAS2E1", true},
{4, 8, 4, 3}: {sys_TLBI, "RIPAS2E1OS", true},
{4, 8, 4, 4}: {sys_TLBI, "IPAS2LE1OS", true},
{4, 8, 4, 6}: {sys_TLBI, "RIPAS2LE1", true},
{4, 8, 4, 7}: {sys_TLBI, "RIPAS2LE1OS", true},
{4, 8, 5, 1}: {sys_TLBI, "RVAE2OS", true},
{4, 8, 5, 5}: {sys_TLBI, "RVALE2OS", true},
{4, 8, 6, 1}: {sys_TLBI, "RVAE2", true},
{4, 8, 6, 5}: {sys_TLBI, "RVALE2", true},
{6, 8, 1, 0}: {sys_TLBI, "ALLE3OS", false},
{6, 8, 1, 1}: {sys_TLBI, "VAE3OS", true},
{6, 8, 1, 5}: {sys_TLBI, "VALE3OS", true},
{6, 8, 2, 1}: {sys_TLBI, "RVAE3IS", true},
{6, 8, 2, 5}: {sys_TLBI, "RVALE3IS", true},
{6, 8, 5, 1}: {sys_TLBI, "RVAE3OS", true},
{6, 8, 5, 5}: {sys_TLBI, "RVALE3OS", true},
{6, 8, 6, 1}: {sys_TLBI, "RVAE3", true},
{6, 8, 6, 5}: {sys_TLBI, "RVALE3", true},
{0, 7, 6, 1}: {sys_DC, "IVAC", true},
{0, 7, 6, 2}: {sys_DC, "ISW", true},
{0, 7, 10, 2}: {sys_DC, "CSW", true},
{0, 7, 14, 2}: {sys_DC, "CISW", true},
{3, 7, 4, 1}: {sys_DC, "ZVA", true},
{3, 7, 10, 1}: {sys_DC, "CVAC", true},
{3, 7, 11, 1}: {sys_DC, "CVAU", true},
{3, 7, 14, 1}: {sys_DC, "CIVAC", true},
{0, 7, 6, 3}: {sys_DC, "IGVAC", true},
{0, 7, 6, 4}: {sys_DC, "IGSW", true},
{0, 7, 6, 5}: {sys_DC, "IGDVAC", true},
{0, 7, 6, 6}: {sys_DC, "IGDSW", true},
{0, 7, 10, 4}: {sys_DC, "CGSW", true},
{0, 7, 10, 6}: {sys_DC, "CGDSW", true},
{0, 7, 14, 4}: {sys_DC, "CIGSW", true},
{0, 7, 14, 6}: {sys_DC, "CIGDSW", true},
{3, 7, 4, 3}: {sys_DC, "GVA", true},
{3, 7, 4, 4}: {sys_DC, "GZVA", true},
{3, 7, 10, 3}: {sys_DC, "CGVAC", true},
{3, 7, 10, 5}: {sys_DC, "CGDVAC", true},
{3, 7, 12, 3}: {sys_DC, "CGVAP", true},
{3, 7, 12, 5}: {sys_DC, "CGDVAP", true},
{3, 7, 13, 3}: {sys_DC, "CGVADP", true},
{3, 7, 13, 5}: {sys_DC, "CGDVADP", true},
{3, 7, 14, 3}: {sys_DC, "CIGVAC", true},
{3, 7, 14, 5}: {sys_DC, "CIGDVAC", true},
{3, 7, 12, 1}: {sys_DC, "CVAP", true},
{3, 7, 13, 1}: {sys_DC, "CVADP", true},
}

View file

@ -142,7 +142,7 @@ func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) strin
case LRVH:
op = "MOVHBR"
}
case LA, LAY:
case LA, LAY, LARL:
args[0], args[1] = args[1], args[0]
op = "MOVD"
@ -349,6 +349,17 @@ func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) strin
case SLBGR:
op = "SUBE"
args[0], args[1] = args[1], args[0]
case MADBR:
op = "FMADD"
args[0], args[1], args[2] = args[1], args[2], args[0]
case VFM:
op = "WFMDB"
args[0], args[1], args[2] = args[1], args[2], args[0]
args = args[0:3]
case VFS:
op = "WFSDB"
args[0], args[2] = args[2], args[0]
args = args[0:3]
case MSGFR, MHI, MSFI, MSGFI:
switch inst.Op {
case MSGFR, MHI, MSFI:
@ -500,16 +511,16 @@ func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) strin
if err != nil {
return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
}
opStr, check := branchOnConditionOp(mask, inst.Op)
opStr := branchOnConditionOp(mask, inst.Op)
if opStr != "" {
op = opStr
}
if op == "SYNC" || op == "NOPH" {
return op
}
if check {
args[0] = args[1]
args = args[:1]
if op == "RET" {
args = args[:0]
return op
}
case LOCGR:
mask, err := strconv.Atoi(args[2][1:])
@ -1036,6 +1047,9 @@ func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) strin
// branch on relative mnemonic.
func branch_relative_op(mask int, opconst Op) (op string, check bool) {
switch mask & 0xf {
case 1:
op = "BVS"
check = true
case 2:
op = "BGT"
check = true
@ -1061,7 +1075,7 @@ func branch_relative_op(mask int, opconst Op) (op string, check bool) {
op = "BLEU"
check = true
case 15:
op = "JMP" // BR
op = "BR"
check = true
}
return op, check
@ -1069,17 +1083,16 @@ func branch_relative_op(mask int, opconst Op) (op string, check bool) {
// This function returns corresponding extended mnemonic for the given
// brach on condition mnemonic.
func branchOnConditionOp(mask int, opconst Op) (op string, check bool) {
func branchOnConditionOp(mask int, opconst Op) (op string) {
switch mask & 0xf {
case 0:
op = "NOPH"
case 14:
op = "SYNC"
case 15:
op = "JMP"
check = true
op = "RET"
}
return op, check
return op
}
// This function returns corresponding plan9 mnemonic for the native bitwise mnemonic.
@ -1260,7 +1273,7 @@ func reverseOperandOrder(op Op) bool {
switch op {
case LOCR, MLGR:
return true
case LTEBR, LTDBR:
case LTEBR, LTDBR, LCDBR, LGDR:
return true
case VLEIB, VLEIH, VLEIF, VLEIG, VPDI:
return true

View file

@ -667,6 +667,7 @@ func gnuArg(inst *Inst, pc uint64, symname SymLookup, x Arg, usedPrefixes *bool)
}
}
case Imm:
if (inst.Op == MOV || inst.Op == PUSH) && inst.DataSize == 32 { // See comment in plan9x.go.
if s, base := symname(uint64(x)); s != "" {
suffix := ""
if uint64(x) != base {
@ -674,6 +675,7 @@ func gnuArg(inst *Inst, pc uint64, symname SymLookup, x Arg, usedPrefixes *bool)
}
return fmt.Sprintf("$%s%s", s, suffix)
}
}
if inst.Mode == 32 {
return fmt.Sprintf("$%#x", uint32(x))
}

View file

@ -341,6 +341,7 @@ func IntelSyntax(inst Inst, pc uint64, symname SymLookup) string {
func intelArg(inst *Inst, pc uint64, symname SymLookup, arg Arg) string {
switch a := arg.(type) {
case Imm:
if (inst.Op == MOV || inst.Op == PUSH) && inst.DataSize == 32 { // See comment in plan9x.go.
if s, base := symname(uint64(a)); s != "" {
suffix := ""
if uint64(a) != base {
@ -348,6 +349,7 @@ func intelArg(inst *Inst, pc uint64, symname SymLookup, arg Arg) string {
}
return fmt.Sprintf("$%s%s", s, suffix)
}
}
if inst.Mode == 32 {
return fmt.Sprintf("%#x", uint32(a))
}

View file

@ -116,6 +116,16 @@ func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg
return fmt.Sprintf("%#x", addr)
case Imm:
if (inst.Op == MOV || inst.Op == PUSH) && inst.DataSize == 32 {
// Only try to convert an immediate to a symbol in certain
// special circumstances. See issue 72942.
//
// On 64-bit, symbol addresses always hit the Mem case below.
// Particularly, we use LEAQ to materialize the address of
// a global or function.
//
// On 32-bit, we sometimes use MOVL. Still try to symbolize
// those immediates.
if s, base := symname(uint64(a)); s != "" {
suffix := ""
if uint64(a) != base {
@ -123,6 +133,7 @@ func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg
}
return fmt.Sprintf("$%s%s(SB)", s, suffix)
}
}
if inst.Mode == 32 {
return fmt.Sprintf("$%#x", uint32(a))
}

View file

@ -12,6 +12,8 @@ package errgroup
import (
"context"
"fmt"
"runtime"
"runtime/debug"
"sync"
)
@ -31,6 +33,10 @@ type Group struct {
errOnce sync.Once
err error
mu sync.Mutex
panicValue any // = PanicError | PanicValue; non-nil if some Group.Go goroutine panicked.
abnormal bool // some Group.Go goroutine terminated abnormally (panic or goexit).
}
func (g *Group) done() {
@ -50,33 +56,78 @@ func WithContext(ctx context.Context) (*Group, context.Context) {
return &Group{cancel: cancel}, ctx
}
// Wait blocks until all function calls from the Go method have returned, then
// returns the first non-nil error (if any) from them.
// Wait blocks until all function calls from the Go method have returned
// normally, then returns the first non-nil error (if any) from them.
//
// If any of the calls panics, Wait panics with a [PanicValue];
// and if any of them calls [runtime.Goexit], Wait calls runtime.Goexit.
func (g *Group) Wait() error {
g.wg.Wait()
if g.cancel != nil {
g.cancel(g.err)
}
if g.panicValue != nil {
panic(g.panicValue)
}
if g.abnormal {
runtime.Goexit()
}
return g.err
}
// Go calls the given function in a new goroutine.
//
// The first call to Go must happen before a Wait.
// It blocks until the new goroutine can be added without the number of
// active goroutines in the group exceeding the configured limit.
// goroutines in the group exceeding the configured limit.
//
// The first call to return a non-nil error cancels the group's context, if the
// group was created by calling WithContext. The error will be returned by Wait.
// The first goroutine in the group that returns a non-nil error, panics, or
// invokes [runtime.Goexit] will cancel the associated Context, if any.
func (g *Group) Go(f func() error) {
if g.sem != nil {
g.sem <- token{}
}
g.add(f)
}
func (g *Group) add(f func() error) {
g.wg.Add(1)
go func() {
defer g.done()
normalReturn := false
defer func() {
if normalReturn {
return
}
v := recover()
g.mu.Lock()
defer g.mu.Unlock()
if !g.abnormal {
if g.cancel != nil {
g.cancel(g.err)
}
g.abnormal = true
}
if v != nil && g.panicValue == nil {
switch v := v.(type) {
case error:
g.panicValue = PanicError{
Recovered: v,
Stack: debug.Stack(),
}
default:
g.panicValue = PanicValue{
Recovered: v,
Stack: debug.Stack(),
}
}
}
}()
if err := f(); err != nil {
err := f()
normalReturn = true
if err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
@ -101,19 +152,7 @@ func (g *Group) TryGo(f func() error) bool {
}
}
g.wg.Add(1)
go func() {
defer g.done()
if err := f(); err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
g.cancel(g.err)
}
})
}
}()
g.add(f)
return true
}
@ -135,3 +174,34 @@ func (g *Group) SetLimit(n int) {
}
g.sem = make(chan token, n)
}
// PanicError wraps an error recovered from an unhandled panic
// when calling a function passed to Go or TryGo.
type PanicError struct {
Recovered error
Stack []byte // result of call to [debug.Stack]
}
func (p PanicError) Error() string {
if len(p.Stack) > 0 {
return fmt.Sprintf("recovered from errgroup.Group: %v\n%s", p.Recovered, p.Stack)
}
return fmt.Sprintf("recovered from errgroup.Group: %v", p.Recovered)
}
func (p PanicError) Unwrap() error { return p.Recovered }
// PanicValue wraps a value that does not implement the error interface,
// recovered from an unhandled panic when calling a function passed to Go or
// TryGo.
type PanicValue struct {
Recovered any
Stack []byte // result of call to [debug.Stack]
}
func (p PanicValue) String() string {
if len(p.Stack) > 0 {
return fmt.Sprintf("recovered from errgroup.Group: %v\n%s", p.Recovered, p.Stack)
}
return fmt.Sprintf("recovered from errgroup.Group: %v", p.Recovered)
}

View file

@ -1303,7 +1303,10 @@ func (selfRelativeSD *SECURITY_DESCRIPTOR) ToAbsolute() (absoluteSD *SECURITY_DE
return nil, err
}
if absoluteSDSize > 0 {
absoluteSD = (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&make([]byte, absoluteSDSize)[0]))
absoluteSD = new(SECURITY_DESCRIPTOR)
if unsafe.Sizeof(*absoluteSD) < uintptr(absoluteSDSize) {
panic("sizeof(SECURITY_DESCRIPTOR) too small")
}
}
var (
dacl *ACL
@ -1312,19 +1315,55 @@ func (selfRelativeSD *SECURITY_DESCRIPTOR) ToAbsolute() (absoluteSD *SECURITY_DE
group *SID
)
if daclSize > 0 {
dacl = (*ACL)(unsafe.Pointer(&make([]byte, daclSize)[0]))
dacl = (*ACL)(unsafe.Pointer(unsafe.SliceData(make([]byte, daclSize))))
}
if saclSize > 0 {
sacl = (*ACL)(unsafe.Pointer(&make([]byte, saclSize)[0]))
sacl = (*ACL)(unsafe.Pointer(unsafe.SliceData(make([]byte, saclSize))))
}
if ownerSize > 0 {
owner = (*SID)(unsafe.Pointer(&make([]byte, ownerSize)[0]))
owner = (*SID)(unsafe.Pointer(unsafe.SliceData(make([]byte, ownerSize))))
}
if groupSize > 0 {
group = (*SID)(unsafe.Pointer(&make([]byte, groupSize)[0]))
group = (*SID)(unsafe.Pointer(unsafe.SliceData(make([]byte, groupSize))))
}
// We call into Windows via makeAbsoluteSD, which sets up
// pointers within absoluteSD that point to other chunks of memory
// we pass into makeAbsoluteSD, and that happens outside the view of the GC.
// We therefore take some care here to then verify the pointers are as we expect
// and set them explicitly in view of the GC. See https://go.dev/issue/73199.
// TODO: consider weak pointers once Go 1.24 is appropriate. See suggestion in https://go.dev/cl/663575.
err = makeAbsoluteSD(selfRelativeSD, absoluteSD, &absoluteSDSize,
dacl, &daclSize, sacl, &saclSize, owner, &ownerSize, group, &groupSize)
if err != nil {
// Don't return absoluteSD, which might be partially initialized.
return nil, err
}
// Before using any fields, verify absoluteSD is in the format we expect according to Windows.
// See https://learn.microsoft.com/en-us/windows/win32/secauthz/absolute-and-self-relative-security-descriptors
absControl, _, err := absoluteSD.Control()
if err != nil {
panic("absoluteSD: " + err.Error())
}
if absControl&SE_SELF_RELATIVE != 0 {
panic("absoluteSD not in absolute format")
}
if absoluteSD.dacl != dacl {
panic("dacl pointer mismatch")
}
if absoluteSD.sacl != sacl {
panic("sacl pointer mismatch")
}
if absoluteSD.owner != owner {
panic("owner pointer mismatch")
}
if absoluteSD.group != group {
panic("group pointer mismatch")
}
absoluteSD.dacl = dacl
absoluteSD.sacl = sacl
absoluteSD.owner = owner
absoluteSD.group = group
return
}

View file

@ -870,6 +870,7 @@ const socket_error = uintptr(^uint32(0))
//sys WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSARecvFrom
//sys WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSASendTo
//sys WSASocket(af int32, typ int32, protocol int32, protoInfo *WSAProtocolInfo, group uint32, flags uint32) (handle Handle, err error) [failretval==InvalidHandle] = ws2_32.WSASocketW
//sys WSADuplicateSocket(s Handle, processID uint32, info *WSAProtocolInfo) (err error) [failretval!=0] = ws2_32.WSADuplicateSocketW
//sys GetHostByName(name string) (h *Hostent, err error) [failretval==nil] = ws2_32.gethostbyname
//sys GetServByName(name string, proto string) (s *Servent, err error) [failretval==nil] = ws2_32.getservbyname
//sys Ntohs(netshort uint16) (u uint16) = ws2_32.ntohs
@ -1698,8 +1699,9 @@ func NewNTUnicodeString(s string) (*NTUnicodeString, error) {
// Slice returns a uint16 slice that aliases the data in the NTUnicodeString.
func (s *NTUnicodeString) Slice() []uint16 {
slice := unsafe.Slice(s.Buffer, s.MaximumLength)
return slice[:s.Length]
// Note: this rounds the length down, if it happens
// to (incorrectly) be odd. Probably safer than rounding up.
return unsafe.Slice(s.Buffer, s.MaximumLength/2)[:s.Length/2]
}
func (s *NTUnicodeString) String() string {

View file

@ -2700,6 +2700,8 @@ type CommTimeouts struct {
// NTUnicodeString is a UTF-16 string for NT native APIs, corresponding to UNICODE_STRING.
type NTUnicodeString struct {
// Note: Length and MaximumLength are in *bytes*, not uint16s.
// They should always be even.
Length uint16
MaximumLength uint16
Buffer *uint16
@ -3628,3 +3630,213 @@ const (
KLF_NOTELLSHELL = 0x00000080
KLF_SETFORPROCESS = 0x00000100
)
// Virtual Key codes
// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
const (
VK_LBUTTON = 0x01
VK_RBUTTON = 0x02
VK_CANCEL = 0x03
VK_MBUTTON = 0x04
VK_XBUTTON1 = 0x05
VK_XBUTTON2 = 0x06
VK_BACK = 0x08
VK_TAB = 0x09
VK_CLEAR = 0x0C
VK_RETURN = 0x0D
VK_SHIFT = 0x10
VK_CONTROL = 0x11
VK_MENU = 0x12
VK_PAUSE = 0x13
VK_CAPITAL = 0x14
VK_KANA = 0x15
VK_HANGEUL = 0x15
VK_HANGUL = 0x15
VK_IME_ON = 0x16
VK_JUNJA = 0x17
VK_FINAL = 0x18
VK_HANJA = 0x19
VK_KANJI = 0x19
VK_IME_OFF = 0x1A
VK_ESCAPE = 0x1B
VK_CONVERT = 0x1C
VK_NONCONVERT = 0x1D
VK_ACCEPT = 0x1E
VK_MODECHANGE = 0x1F
VK_SPACE = 0x20
VK_PRIOR = 0x21
VK_NEXT = 0x22
VK_END = 0x23
VK_HOME = 0x24
VK_LEFT = 0x25
VK_UP = 0x26
VK_RIGHT = 0x27
VK_DOWN = 0x28
VK_SELECT = 0x29
VK_PRINT = 0x2A
VK_EXECUTE = 0x2B
VK_SNAPSHOT = 0x2C
VK_INSERT = 0x2D
VK_DELETE = 0x2E
VK_HELP = 0x2F
VK_LWIN = 0x5B
VK_RWIN = 0x5C
VK_APPS = 0x5D
VK_SLEEP = 0x5F
VK_NUMPAD0 = 0x60
VK_NUMPAD1 = 0x61
VK_NUMPAD2 = 0x62
VK_NUMPAD3 = 0x63
VK_NUMPAD4 = 0x64
VK_NUMPAD5 = 0x65
VK_NUMPAD6 = 0x66
VK_NUMPAD7 = 0x67
VK_NUMPAD8 = 0x68
VK_NUMPAD9 = 0x69
VK_MULTIPLY = 0x6A
VK_ADD = 0x6B
VK_SEPARATOR = 0x6C
VK_SUBTRACT = 0x6D
VK_DECIMAL = 0x6E
VK_DIVIDE = 0x6F
VK_F1 = 0x70
VK_F2 = 0x71
VK_F3 = 0x72
VK_F4 = 0x73
VK_F5 = 0x74
VK_F6 = 0x75
VK_F7 = 0x76
VK_F8 = 0x77
VK_F9 = 0x78
VK_F10 = 0x79
VK_F11 = 0x7A
VK_F12 = 0x7B
VK_F13 = 0x7C
VK_F14 = 0x7D
VK_F15 = 0x7E
VK_F16 = 0x7F
VK_F17 = 0x80
VK_F18 = 0x81
VK_F19 = 0x82
VK_F20 = 0x83
VK_F21 = 0x84
VK_F22 = 0x85
VK_F23 = 0x86
VK_F24 = 0x87
VK_NUMLOCK = 0x90
VK_SCROLL = 0x91
VK_OEM_NEC_EQUAL = 0x92
VK_OEM_FJ_JISHO = 0x92
VK_OEM_FJ_MASSHOU = 0x93
VK_OEM_FJ_TOUROKU = 0x94
VK_OEM_FJ_LOYA = 0x95
VK_OEM_FJ_ROYA = 0x96
VK_LSHIFT = 0xA0
VK_RSHIFT = 0xA1
VK_LCONTROL = 0xA2
VK_RCONTROL = 0xA3
VK_LMENU = 0xA4
VK_RMENU = 0xA5
VK_BROWSER_BACK = 0xA6
VK_BROWSER_FORWARD = 0xA7
VK_BROWSER_REFRESH = 0xA8
VK_BROWSER_STOP = 0xA9
VK_BROWSER_SEARCH = 0xAA
VK_BROWSER_FAVORITES = 0xAB
VK_BROWSER_HOME = 0xAC
VK_VOLUME_MUTE = 0xAD
VK_VOLUME_DOWN = 0xAE
VK_VOLUME_UP = 0xAF
VK_MEDIA_NEXT_TRACK = 0xB0
VK_MEDIA_PREV_TRACK = 0xB1
VK_MEDIA_STOP = 0xB2
VK_MEDIA_PLAY_PAUSE = 0xB3
VK_LAUNCH_MAIL = 0xB4
VK_LAUNCH_MEDIA_SELECT = 0xB5
VK_LAUNCH_APP1 = 0xB6
VK_LAUNCH_APP2 = 0xB7
VK_OEM_1 = 0xBA
VK_OEM_PLUS = 0xBB
VK_OEM_COMMA = 0xBC
VK_OEM_MINUS = 0xBD
VK_OEM_PERIOD = 0xBE
VK_OEM_2 = 0xBF
VK_OEM_3 = 0xC0
VK_OEM_4 = 0xDB
VK_OEM_5 = 0xDC
VK_OEM_6 = 0xDD
VK_OEM_7 = 0xDE
VK_OEM_8 = 0xDF
VK_OEM_AX = 0xE1
VK_OEM_102 = 0xE2
VK_ICO_HELP = 0xE3
VK_ICO_00 = 0xE4
VK_PROCESSKEY = 0xE5
VK_ICO_CLEAR = 0xE6
VK_OEM_RESET = 0xE9
VK_OEM_JUMP = 0xEA
VK_OEM_PA1 = 0xEB
VK_OEM_PA2 = 0xEC
VK_OEM_PA3 = 0xED
VK_OEM_WSCTRL = 0xEE
VK_OEM_CUSEL = 0xEF
VK_OEM_ATTN = 0xF0
VK_OEM_FINISH = 0xF1
VK_OEM_COPY = 0xF2
VK_OEM_AUTO = 0xF3
VK_OEM_ENLW = 0xF4
VK_OEM_BACKTAB = 0xF5
VK_ATTN = 0xF6
VK_CRSEL = 0xF7
VK_EXSEL = 0xF8
VK_EREOF = 0xF9
VK_PLAY = 0xFA
VK_ZOOM = 0xFB
VK_NONAME = 0xFC
VK_PA1 = 0xFD
VK_OEM_CLEAR = 0xFE
)
// Mouse button constants.
// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str
const (
FROM_LEFT_1ST_BUTTON_PRESSED = 0x0001
RIGHTMOST_BUTTON_PRESSED = 0x0002
FROM_LEFT_2ND_BUTTON_PRESSED = 0x0004
FROM_LEFT_3RD_BUTTON_PRESSED = 0x0008
FROM_LEFT_4TH_BUTTON_PRESSED = 0x0010
)
// Control key state constaints.
// https://docs.microsoft.com/en-us/windows/console/key-event-record-str
// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str
const (
CAPSLOCK_ON = 0x0080
ENHANCED_KEY = 0x0100
LEFT_ALT_PRESSED = 0x0002
LEFT_CTRL_PRESSED = 0x0008
NUMLOCK_ON = 0x0020
RIGHT_ALT_PRESSED = 0x0001
RIGHT_CTRL_PRESSED = 0x0004
SCROLLLOCK_ON = 0x0040
SHIFT_PRESSED = 0x0010
)
// Mouse event record event flags.
// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str
const (
MOUSE_MOVED = 0x0001
DOUBLE_CLICK = 0x0002
MOUSE_WHEELED = 0x0004
MOUSE_HWHEELED = 0x0008
)
// Input Record Event Types
// https://learn.microsoft.com/en-us/windows/console/input-record-str
const (
FOCUS_EVENT = 0x0010
KEY_EVENT = 0x0001
MENU_EVENT = 0x0008
MOUSE_EVENT = 0x0002
WINDOW_BUFFER_SIZE_EVENT = 0x0004
)

View file

@ -511,6 +511,7 @@ var (
procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW")
procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW")
procWSACleanup = modws2_32.NewProc("WSACleanup")
procWSADuplicateSocketW = modws2_32.NewProc("WSADuplicateSocketW")
procWSAEnumProtocolsW = modws2_32.NewProc("WSAEnumProtocolsW")
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
procWSAIoctl = modws2_32.NewProc("WSAIoctl")
@ -4391,6 +4392,14 @@ func WSACleanup() (err error) {
return
}
func WSADuplicateSocket(s Handle, processID uint32, info *WSAProtocolInfo) (err error) {
r1, _, e1 := syscall.Syscall(procWSADuplicateSocketW.Addr(), 3, uintptr(s), uintptr(processID), uintptr(unsafe.Pointer(info)))
if r1 != 0 {
err = errnoErr(e1)
}
return
}
func WSAEnumProtocols(protocols *int32, protocolBuffer *WSAProtocolInfo, bufferLength *uint32) (n int32, err error) {
r0, _, e1 := syscall.Syscall(procWSAEnumProtocolsW.Addr(), 3, uintptr(unsafe.Pointer(protocols)), uintptr(unsafe.Pointer(protocolBuffer)), uintptr(unsafe.Pointer(bufferLength)))
n = int32(r0)

View file

@ -8,6 +8,7 @@ package countertest
import (
"sync"
"testing"
"golang.org/x/telemetry/counter"
ic "golang.org/x/telemetry/internal/counter"
@ -61,3 +62,10 @@ func ReadStackCounter(c *counter.StackCounter) (stackCounts map[string]uint64, _
func ReadFile(name string) (counters, stackCounters map[string]uint64, _ error) {
return ic.ReadFile(name)
}
func init() {
// Extra safety check.
if !testing.Testing() {
panic("use of this package is disallowed in non-testing code")
}
}

View file

@ -1,16 +0,0 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.21
package countertest
import "testing"
func init() {
// Extra safety check for go1.21+.
if !testing.Testing() {
panic("use of this package is disallowed in non-testing code")
}
}

View file

@ -93,12 +93,22 @@ func EncodeStack(pcs []uintptr, prefix string) string {
// Use function-relative line numbering.
// f:+2 means two lines into function f.
// f:-1 should never happen, but be conservative.
//
// An inlined call is replaced by a NOP instruction
// with the correct pclntab information.
_, entryLine := fr.Func.FileLine(fr.Entry)
loc = fmt.Sprintf("%s.%s:%+d", path, fname, fr.Line-entryLine)
loc = fmt.Sprintf("%s.%s:%+d,+0x%x", path, fname, fr.Line-entryLine, fr.PC-fr.Entry)
} else {
// The function is non-Go code or is fully inlined:
// use absolute line number within enclosing file.
loc = fmt.Sprintf("%s.%s:=%d", path, fname, fr.Line)
//
// For inlined calls, the PC and Entry values
// both refer to the enclosing combined function.
// For example, both these PCs are relative to "caller":
//
// callee:=1,+0x12 ('=' means inlined)
// caller:+2,+0x34
loc = fmt.Sprintf("%s.%s:=%d,+0x%x", path, fname, fr.Line, fr.PC-fr.Entry)
}
locs = append(locs, loc)
if !more {

View file

@ -1,17 +0,0 @@
// Copyright 2024 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.
//go:build go1.23
// +build go1.23
package crashmonitor
import (
"os"
"runtime/debug"
)
func init() {
setCrashOutput = func(f *os.File) error { return debug.SetCrashOutput(f, debug.CrashOptions{}) }
}

View file

@ -21,20 +21,13 @@ import (
"golang.org/x/telemetry/internal/counter"
)
// Supported reports whether the runtime supports [runtime/debug.SetCrashOutput].
//
// TODO(adonovan): eliminate once go1.23+ is assured.
func Supported() bool { return setCrashOutput != nil }
var setCrashOutput func(*os.File) error // = runtime/debug.SetCrashOutput on go1.23+
// Parent sets up the parent side of the crashmonitor. It requires
// exclusive use of a writable pipe connected to the child process's stdin.
func Parent(pipe *os.File) {
writeSentinel(pipe)
// Ensure that we get pc=0x%x values in the traceback.
debug.SetTraceback("system")
setCrashOutput(pipe)
debug.SetCrashOutput(pipe, debug.CrashOptions{}) // ignore error
}
// Child runs the part of the crashmonitor that runs in the child process.
@ -284,7 +277,7 @@ func parseStackPCs(crash string) ([]uintptr, error) {
continue
}
pc = pc-parentSentinel+childSentinel
pc = pc - parentSentinel + childSentinel
// If the previous frame was sigpanic, then this frame
// was a trap (e.g., SIGSEGV).

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build (js && wasm) || wasip1 || plan9 || (solaris && !go1.20)
//go:build (js && wasm) || wasip1 || plan9
package mmap

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build unix && (!solaris || go1.20)
//go:build unix
package mmap

View file

@ -5,6 +5,7 @@
package telemetry
import (
"go/version"
"os"
"path/filepath"
"runtime/debug"
@ -26,9 +27,7 @@ func IsToolchainProgram(progPath string) bool {
// special characters.
func ProgramInfo(info *debug.BuildInfo) (goVers, progPath, progVers string) {
goVers = info.GoVersion
// TODO(matloob): Use go/version.IsValid instead of checking for X: once the telemetry
// module can be upgraded to require Go 1.22.
if strings.Contains(goVers, "devel") || strings.Contains(goVers, "-") || strings.Contains(goVers, "X:") {
if strings.Contains(goVers, "devel") || strings.Contains(goVers, "-") || !version.IsValid(goVers) {
goVers = "devel"
}

View file

@ -166,7 +166,7 @@ func parent(config Config) *StartResult {
}
childShouldUpload := config.Upload && acquireUploadToken()
reportCrashes := config.ReportCrashes && crashmonitor.Supported()
reportCrashes := config.ReportCrashes
if reportCrashes || childShouldUpload {
startChild(reportCrashes, childShouldUpload, result)
@ -267,10 +267,6 @@ func child(config Config) {
os.Setenv(telemetryChildVar, "2")
upload := os.Getenv(telemetryUploadVar) == "1"
reportCrashes := config.ReportCrashes && crashmonitor.Supported()
uploadStartTime := config.UploadStartTime
uploadURL := config.UploadURL
// The crashmonitor and/or upload process may themselves record counters.
counter.Open()
@ -280,7 +276,7 @@ func child(config Config) {
// upload to finish before exiting
var g errgroup.Group
if reportCrashes {
if config.ReportCrashes {
g.Go(func() error {
crashmonitor.Child()
return nil
@ -288,7 +284,7 @@ func child(config Config) {
}
if upload {
g.Go(func() error {
uploaderChild(uploadStartTime, uploadURL)
uploaderChild(config.UploadStartTime, config.UploadURL)
return nil
})
}

View file

@ -6,6 +6,7 @@ package term
import (
"bytes"
"fmt"
"io"
"runtime"
"strconv"
@ -36,6 +37,26 @@ var vt100EscapeCodes = EscapeCodes{
Reset: []byte{keyEscape, '[', '0', 'm'},
}
// A History provides a (possibly bounded) queue of input lines read by [Terminal.ReadLine].
type History interface {
// Add will be called by [Terminal.ReadLine] to add
// a new, most recent entry to the history.
// It is allowed to drop any entry, including
// the entry being added (e.g., if it's deemed an invalid entry),
// the least-recent entry (e.g., to keep the history bounded),
// or any other entry.
Add(entry string)
// Len returns the number of entries in the history.
Len() int
// At returns an entry from the history.
// Index 0 is the most-recently added entry and
// index Len()-1 is the least-recently added entry.
// If index is < 0 or >= Len(), it panics.
At(idx int) string
}
// Terminal contains the state for running a VT100 terminal that is capable of
// reading lines of input.
type Terminal struct {
@ -44,6 +65,8 @@ type Terminal struct {
// bytes, as an index into |line|). If it returns ok=false, the key
// press is processed normally. Otherwise it returns a replacement line
// and the new cursor position.
//
// This will be disabled during ReadPassword.
AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
// Escape contains a pointer to the escape codes for this terminal.
@ -84,9 +107,14 @@ type Terminal struct {
remainder []byte
inBuf [256]byte
// history contains previously entered commands so that they can be
// accessed with the up and down keys.
history stRingBuffer
// History records and retrieves lines of input read by [ReadLine] which
// a user can retrieve and navigate using the up and down arrow keys.
//
// It is not safe to call ReadLine concurrently with any methods on History.
//
// [NewTerminal] sets this to a default implementation that records the
// last 100 lines of input.
History History
// historyIndex stores the currently accessed history entry, where zero
// means the immediately previous entry.
historyIndex int
@ -109,6 +137,7 @@ func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
termHeight: 24,
echo: true,
historyIndex: -1,
History: &stRingBuffer{},
}
}
@ -448,6 +477,23 @@ func visualLength(runes []rune) int {
return length
}
// histroryAt unlocks the terminal and relocks it while calling History.At.
func (t *Terminal) historyAt(idx int) (string, bool) {
t.lock.Unlock() // Unlock to avoid deadlock if History methods use the output writer.
defer t.lock.Lock() // panic in At (or Len) protection.
if idx < 0 || idx >= t.History.Len() {
return "", false
}
return t.History.At(idx), true
}
// historyAdd unlocks the terminal and relocks it while calling History.Add.
func (t *Terminal) historyAdd(entry string) {
t.lock.Unlock() // Unlock to avoid deadlock if History methods use the output writer.
defer t.lock.Lock() // panic in Add protection.
t.History.Add(entry)
}
// handleKey processes the given key and, optionally, returns a line of text
// that the user has entered.
func (t *Terminal) handleKey(key rune) (line string, ok bool) {
@ -495,7 +541,7 @@ func (t *Terminal) handleKey(key rune) (line string, ok bool) {
t.pos = len(t.line)
t.moveCursorToPos(t.pos)
case keyUp:
entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
entry, ok := t.historyAt(t.historyIndex + 1)
if !ok {
return "", false
}
@ -514,7 +560,7 @@ func (t *Terminal) handleKey(key rune) (line string, ok bool) {
t.setLine(runes, len(runes))
t.historyIndex--
default:
entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
entry, ok := t.historyAt(t.historyIndex - 1)
if ok {
t.historyIndex--
runes := []rune(entry)
@ -692,6 +738,8 @@ func (t *Terminal) Write(buf []byte) (n int, err error) {
// ReadPassword temporarily changes the prompt and reads a password, without
// echo, from the terminal.
//
// The AutoCompleteCallback is disabled during this call.
func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
t.lock.Lock()
defer t.lock.Unlock()
@ -699,6 +747,11 @@ func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
oldPrompt := t.prompt
t.prompt = []rune(prompt)
t.echo = false
oldAutoCompleteCallback := t.AutoCompleteCallback
t.AutoCompleteCallback = nil
defer func() {
t.AutoCompleteCallback = oldAutoCompleteCallback
}()
line, err = t.readLine()
@ -772,7 +825,7 @@ func (t *Terminal) readLine() (line string, err error) {
if lineOk {
if t.echo {
t.historyIndex = -1
t.history.Add(line)
t.historyAdd(line)
}
if lineIsPasted {
err = ErrPasteIndicator
@ -929,19 +982,23 @@ func (s *stRingBuffer) Add(a string) {
}
}
// NthPreviousEntry returns the value passed to the nth previous call to Add.
func (s *stRingBuffer) Len() int {
return s.size
}
// At returns the value passed to the nth previous call to Add.
// If n is zero then the immediately prior value is returned, if one, then the
// next most recent, and so on. If such an element doesn't exist then ok is
// false.
func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
func (s *stRingBuffer) At(n int) string {
if n < 0 || n >= s.size {
return "", false
panic(fmt.Sprintf("term: history index [%d] out of range [0,%d)", n, s.size))
}
index := s.head - n
if index < 0 {
index += s.max
}
return s.entries[index], true
return s.entries[index]
}
// readPasswordLine reads from reader until it finds \n or io.EOF.

View file

@ -57,7 +57,7 @@ type asmArch struct {
// include the first integer register and first floating-point register. Accessing
// any of them counts as writing to result.
retRegs []string
// writeResult is a list of instructions that will change result register implicity.
// writeResult is a list of instructions that will change result register implicitly.
writeResult []string
// calculated during initialization
sizes types.Sizes

View file

@ -153,7 +153,8 @@ func isLocalType(pass *analysis.Pass, typ types.Type) bool {
return isLocalType(pass, x.Elem())
case interface{ Obj() *types.TypeName }: // *Named or *TypeParam (aliases were removed already)
// names in package foo are local to foo_test too
return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
return x.Obj().Pkg() != nil &&
strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
}
return false
}

View file

@ -30,7 +30,7 @@ var Analyzer = &analysis.Analyzer{
type arch struct {
isFPWrite func(string) bool
isFPRead func(string) bool
isBranch func(string) bool
isUnconditionalBranch func(string) bool
}
var re = regexp.MustCompile
@ -48,8 +48,8 @@ var arches = map[string]arch{
"amd64": {
isFPWrite: re(`,\s*BP$`).MatchString, // TODO: can have false positive, e.g. for TESTQ BP,BP. Seems unlikely.
isFPRead: re(`\bBP\b`).MatchString,
isBranch: func(s string) bool {
return hasAnyPrefix(s, "J", "RET")
isUnconditionalBranch: func(s string) bool {
return hasAnyPrefix(s, "JMP", "RET")
},
},
"arm64": {
@ -70,49 +70,16 @@ var arches = map[string]arch{
return false
},
isFPRead: re(`\bR29\b`).MatchString,
isBranch: func(s string) bool {
isUnconditionalBranch: func(s string) bool {
// Get just the instruction
if i := strings.IndexFunc(s, unicode.IsSpace); i > 0 {
s = s[:i]
}
return arm64Branch[s]
return s == "B" || s == "JMP" || s == "RET"
},
},
}
// arm64 has many control flow instructions.
// ^(B|RET) isn't sufficient or correct (e.g. BIC, BFI aren't control flow.)
// It's easier to explicitly enumerate them in a map than to write a regex.
// Borrowed from Go tree, cmd/asm/internal/arch/arm64.go
var arm64Branch = map[string]bool{
"B": true,
"BL": true,
"BEQ": true,
"BNE": true,
"BCS": true,
"BHS": true,
"BCC": true,
"BLO": true,
"BMI": true,
"BPL": true,
"BVS": true,
"BVC": true,
"BHI": true,
"BLS": true,
"BGE": true,
"BLT": true,
"BGT": true,
"BLE": true,
"CBZ": true,
"CBZW": true,
"CBNZ": true,
"CBNZW": true,
"JMP": true,
"TBNZ": true,
"TBZ": true,
"RET": true,
}
func run(pass *analysis.Pass) (any, error) {
arch, ok := arches[build.Default.GOARCH]
if !ok {
@ -164,7 +131,7 @@ func run(pass *analysis.Pass) (any, error) {
active = false
continue
}
if arch.isFPRead(line) || arch.isBranch(line) {
if arch.isFPRead(line) || arch.isUnconditionalBranch(line) {
active = false
continue
}

View file

@ -88,7 +88,7 @@ func run(pass *analysis.Pass) (any, error) {
//
// TODO: consider allowing the "last" go/defer/Go statement to be followed by
// N "trivial" statements, possibly under a recursive definition of "trivial"
// so that that checker could, for example, conclude that a go statement is
// so that checker could, for example, conclude that a go statement is
// followed by an if statement made of only trivial statements and trivial expressions,
// and hence the go statement could still be checked.
forEachLastStmt(body.List, func(last ast.Stmt) {

View file

@ -22,6 +22,7 @@ import (
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/analysisinternal"
"golang.org/x/tools/internal/astutil"
"golang.org/x/tools/internal/fmtstr"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/versions"
@ -540,7 +541,7 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
firstArg := idx + 1 // Arguments are immediately after format string.
if !strings.Contains(format, "%") {
if len(call.Args) > firstArg {
pass.Reportf(call.Lparen, "%s call has arguments but no formatting directives", name)
pass.ReportRangef(call.Args[firstArg], "%s call has arguments but no formatting directives", name)
}
return
}
@ -552,7 +553,7 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
if err != nil {
// All error messages are in predicate form ("call has a problem")
// so that they may be affixed into a subject ("log.Printf ").
pass.ReportRangef(call.Args[idx], "%s %s", name, err)
pass.ReportRangef(formatArg, "%s %s", name, err)
return
}
@ -560,20 +561,21 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
maxArgIndex := firstArg - 1
anyIndex := false
// Check formats against args.
for _, operation := range operations {
if operation.Prec.Index != -1 ||
operation.Width.Index != -1 ||
operation.Verb.Index != -1 {
for _, op := range operations {
if op.Prec.Index != -1 ||
op.Width.Index != -1 ||
op.Verb.Index != -1 {
anyIndex = true
}
if !okPrintfArg(pass, call, &maxArgIndex, firstArg, name, operation) {
rng := opRange(formatArg, op)
if !okPrintfArg(pass, call, rng, &maxArgIndex, firstArg, name, op) {
// One error per format is enough.
return
}
if operation.Verb.Verb == 'w' {
if op.Verb.Verb == 'w' {
switch kind {
case KindNone, KindPrint, KindPrintf:
pass.Reportf(call.Pos(), "%s does not support error-wrapping directive %%w", name)
pass.ReportRangef(rng, "%s does not support error-wrapping directive %%w", name)
return
}
}
@ -594,6 +596,18 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
}
}
// opRange returns the source range for the specified printf operation,
// such as the position of the %v substring of "...%v...".
func opRange(formatArg ast.Expr, op *fmtstr.Operation) analysis.Range {
if lit, ok := formatArg.(*ast.BasicLit); ok {
start, end, err := astutil.RangeInStringLiteral(lit, op.Range.Start, op.Range.End)
if err == nil {
return analysisinternal.Range(start, end) // position of "%v"
}
}
return formatArg // entire format string
}
// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask.
type printfArgType int
@ -657,7 +671,7 @@ var printVerbs = []printVerb{
// okPrintfArg compares the operation to the arguments actually present,
// reporting any discrepancies it can discern, maxArgIndex was the index of the highest used index.
// If the final argument is ellipsissed, there's little it can do for that.
func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firstArg int, name string, operation *fmtstr.Operation) (ok bool) {
func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, rng analysis.Range, maxArgIndex *int, firstArg int, name string, operation *fmtstr.Operation) (ok bool) {
verb := operation.Verb.Verb
var v printVerb
found := false
@ -680,7 +694,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
if !formatter {
if !found {
pass.ReportRangef(call, "%s format %s has unknown verb %c", name, operation.Text, verb)
pass.ReportRangef(rng, "%s format %s has unknown verb %c", name, operation.Text, verb)
return false
}
for _, flag := range operation.Flags {
@ -690,7 +704,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
continue
}
if !strings.ContainsRune(v.flags, rune(flag)) {
pass.ReportRangef(call, "%s format %s has unrecognized flag %c", name, operation.Text, flag)
pass.ReportRangef(rng, "%s format %s has unrecognized flag %c", name, operation.Text, flag)
return false
}
}
@ -707,7 +721,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
// If len(argIndexes)>0, we have something like %.*s and all
// indexes in argIndexes must be an integer.
for _, argIndex := range argIndexes {
if !argCanBeChecked(pass, call, argIndex, firstArg, operation, name) {
if !argCanBeChecked(pass, call, rng, argIndex, firstArg, operation, name) {
return
}
arg := call.Args[argIndex]
@ -716,7 +730,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
if reason != "" {
details = " (" + reason + ")"
}
pass.ReportRangef(call, "%s format %s uses non-int %s%s as argument of *", name, operation.Text, analysisinternal.Format(pass.Fset, arg), details)
pass.ReportRangef(rng, "%s format %s uses non-int %s%s as argument of *", name, operation.Text, analysisinternal.Format(pass.Fset, arg), details)
return false
}
}
@ -738,12 +752,12 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
// Now check verb's type.
verbArgIndex := operation.Verb.ArgIndex
if !argCanBeChecked(pass, call, verbArgIndex, firstArg, operation, name) {
if !argCanBeChecked(pass, call, rng, verbArgIndex, firstArg, operation, name) {
return false
}
arg := call.Args[verbArgIndex]
if isFunctionValue(pass, arg) && verb != 'p' && verb != 'T' {
pass.ReportRangef(call, "%s format %s arg %s is a func value, not called", name, operation.Text, analysisinternal.Format(pass.Fset, arg))
pass.ReportRangef(rng, "%s format %s arg %s is a func value, not called", name, operation.Text, analysisinternal.Format(pass.Fset, arg))
return false
}
if reason, ok := matchArgType(pass, v.typ, arg); !ok {
@ -755,12 +769,14 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
if reason != "" {
details = " (" + reason + ")"
}
pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s%s", name, operation.Text, analysisinternal.Format(pass.Fset, arg), typeString, details)
pass.ReportRangef(rng, "%s format %s has arg %s of wrong type %s%s", name, operation.Text, analysisinternal.Format(pass.Fset, arg), typeString, details)
return false
}
if v.typ&argString != 0 && v.verb != 'T' && !strings.Contains(operation.Flags, "#") {
// Detect recursive formatting via value's String/Error methods.
// The '#' flag suppresses the methods, except with %x, %X, and %q.
if v.typ&argString != 0 && v.verb != 'T' && (!strings.Contains(operation.Flags, "#") || strings.ContainsRune("qxX", v.verb)) {
if methodName, ok := recursiveStringer(pass, arg); ok {
pass.ReportRangef(call, "%s format %s with arg %s causes recursive %s method call", name, operation.Text, analysisinternal.Format(pass.Fset, arg), methodName)
pass.ReportRangef(rng, "%s format %s with arg %s causes recursive %s method call", name, operation.Text, analysisinternal.Format(pass.Fset, arg), methodName)
return false
}
}
@ -844,7 +860,7 @@ func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
// argCanBeChecked reports whether the specified argument is statically present;
// it may be beyond the list of arguments or in a terminal slice... argument, which
// means we can't see it.
func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, argIndex, firstArg int, operation *fmtstr.Operation, name string) bool {
func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, rng analysis.Range, argIndex, firstArg int, operation *fmtstr.Operation, name string) bool {
if argIndex <= 0 {
// Shouldn't happen, so catch it with prejudice.
panic("negative argIndex")
@ -861,7 +877,7 @@ func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, argIndex, firstArg
// There are bad indexes in the format or there are fewer arguments than the format needs.
// This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi".
arg := argIndex - firstArg + 1 // People think of arguments as 1-indexed.
pass.ReportRangef(call, "%s format %s reads arg #%d, but call has %v", name, operation.Text, arg, count(len(call.Args)-firstArg, "arg"))
pass.ReportRangef(rng, "%s format %s reads arg #%d, but call has %v", name, operation.Text, arg, count(len(call.Args)-firstArg, "arg"))
return false
}

View file

@ -17,6 +17,7 @@ import (
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/internal/analysisinternal"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/typesinternal"
)
//go:embed doc.go
@ -60,12 +61,11 @@ func describe(typ, inType types.Type, inName string) string {
}
func typeName(t types.Type) string {
type hasTypeName interface{ Obj() *types.TypeName } // Alias, Named, TypeParam
switch t := t.(type) {
case *types.Basic:
return t.Name()
case hasTypeName:
return t.Obj().Name()
if basic, ok := t.(*types.Basic); ok {
return basic.Name() // may be (e.g.) "untyped int", which has no TypeName
}
if tname := typesinternal.TypeNameFor(t); tname != nil {
return tname.Name()
}
return ""
}

View file

@ -107,7 +107,7 @@ func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, s
// Embedded struct. Nothing to do for now, but that
// may change, depending on what happens with issue 7363.
// TODO(adonovan): investigate, now that that issue is fixed.
// TODO(adonovan): investigate, now that issue is fixed.
if field.Anonymous() {
return
}

View file

@ -447,18 +447,6 @@ func checkExampleName(pass *analysis.Pass, fn *ast.FuncDecl) {
}
}
type tokenRange struct {
p, e token.Pos
}
func (r tokenRange) Pos() token.Pos {
return r.p
}
func (r tokenRange) End() token.Pos {
return r.e
}
func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) {
// Want functions with 0 results and 1 parameter.
if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
@ -476,8 +464,9 @@ func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) {
if tparams := fn.Type.TypeParams; tparams != nil && len(tparams.List) > 0 {
// Note: cmd/go/internal/load also errors about TestXXX and BenchmarkXXX functions with type parameters.
// We have currently decided to also warn before compilation/package loading. This can help users in IDEs.
at := tokenRange{tparams.Opening, tparams.Closing}
pass.ReportRangef(at, "%s has type parameters: it will not be run by go test as a %sXXX function", fn.Name.Name, prefix)
pass.ReportRangef(analysisinternal.Range(tparams.Opening, tparams.Closing),
"%s has type parameters: it will not be run by go test as a %sXXX function",
fn.Name.Name, prefix)
}
if !isTestSuffix(fn.Name.Name[len(prefix):]) {

View file

@ -26,6 +26,7 @@ import (
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/analysisinternal"
)
//go:embed doc.go
@ -64,17 +65,56 @@ func init() {
"context.WithTimeout": true,
"context.WithValue": true,
"errors.New": true,
"fmt.Append": true,
"fmt.Appendf": true,
"fmt.Appendln": true,
"fmt.Errorf": true,
"fmt.Sprint": true,
"fmt.Sprintf": true,
"fmt.Sprintln": true,
"maps.All": true,
"maps.Clone": true,
"maps.Collect": true,
"maps.Equal": true,
"maps.EqualFunc": true,
"maps.Keys": true,
"maps.Values": true,
"slices.All": true,
"slices.AppendSeq": true,
"slices.Backward": true,
"slices.BinarySearch": true,
"slices.BinarySearchFunc": true,
"slices.Chunk": true,
"slices.Clip": true,
"slices.Clone": true,
"slices.Collect": true,
"slices.Compact": true,
"slices.CompactFunc": true,
"slices.Compare": true,
"slices.CompareFunc": true,
"slices.Concat": true,
"slices.Contains": true,
"slices.ContainsFunc": true,
"slices.Delete": true,
"slices.DeleteFunc": true,
"slices.Equal": true,
"slices.EqualFunc": true,
"slices.Grow": true,
"slices.Index": true,
"slices.IndexFunc": true,
"slices.Insert": true,
"slices.IsSorted": true,
"slices.IsSortedFunc": true,
"slices.Max": true,
"slices.MaxFunc": true,
"slices.Min": true,
"slices.MinFunc": true,
"slices.Repeat": true,
"slices.Replace": true,
"slices.Sorted": true,
"slices.SortedFunc": true,
"slices.SortedStableFunc": true,
"slices.Values": true,
"sort.Reverse": true,
}
Analyzer.Flags.Var(&funcs, "funcs",
@ -114,14 +154,16 @@ func run(pass *analysis.Pass) (any, error) {
// method (e.g. foo.String())
if types.Identical(sig, sigNoArgsStringResult) {
if stringMethods[fn.Name()] {
pass.Reportf(call.Lparen, "result of (%s).%s call not used",
pass.ReportRangef(analysisinternal.Range(call.Pos(), call.Lparen),
"result of (%s).%s call not used",
sig.Recv().Type(), fn.Name())
}
}
} else {
// package-level function (e.g. fmt.Errorf)
if pkgFuncs[[2]string{fn.Pkg().Path(), fn.Name()}] {
pass.Reportf(call.Lparen, "result of %s.%s call not used",
pass.ReportRangef(analysisinternal.Range(call.Pos(), call.Lparen),
"result of %s.%s call not used",
fn.Pkg().Path(), fn.Name())
}
}

View file

@ -1,18 +1,8 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Copyright 2025 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.
//go:build go1.23
// Package cursor augments [inspector.Inspector] with [Cursor]
// functionality allowing more flexibility and control during
// inspection.
//
// This package is a temporary private extension of inspector until
// proposal #70859 is accepted, and which point it will be moved into
// the inspector package, and [Root] will become a method of
// Inspector.
package cursor
package inspector
import (
"fmt"
@ -21,17 +11,29 @@ import (
"iter"
"reflect"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/internal/astutil/edge"
"golang.org/x/tools/go/ast/edge"
)
// A Cursor represents an [ast.Node]. It is immutable.
//
// Two Cursors compare equal if they represent the same node.
//
// Call [Root] to obtain a valid cursor.
// Call [Inspector.Root] to obtain a valid cursor for the virtual root
// node of the traversal.
//
// Use the following methods to navigate efficiently around the tree:
// - for ancestors, use [Cursor.Parent] and [Cursor.Enclosing];
// - for children, use [Cursor.Child], [Cursor.Children],
// [Cursor.FirstChild], and [Cursor.LastChild];
// - for siblings, use [Cursor.PrevSibling] and [Cursor.NextSibling];
// - for descendants, use [Cursor.FindByPos], [Cursor.FindNode],
// [Cursor.Inspect], and [Cursor.Preorder].
//
// Use the [Cursor.ChildAt] and [Cursor.ParentEdge] methods for
// information about the edges in a tree: which field (and slice
// element) of the parent node holds the child.
type Cursor struct {
in *inspector.Inspector
in *Inspector
index int32 // index of push node; -1 for virtual root node
}
@ -39,29 +41,28 @@ type Cursor struct {
// whose children are the files provided to [New].
//
// Its [Cursor.Node] and [Cursor.Stack] methods return nil.
func Root(in *inspector.Inspector) Cursor {
func (in *Inspector) Root() Cursor {
return Cursor{in, -1}
}
// At returns the cursor at the specified index in the traversal,
// which must have been obtained from [Cursor.Index] on a Cursor
// belonging to the same Inspector (see [Cursor.Inspector]).
func At(in *inspector.Inspector, index int32) Cursor {
func (in *Inspector) At(index int32) Cursor {
if index < 0 {
panic("negative index")
}
events := events(in)
if int(index) >= len(events) {
if int(index) >= len(in.events) {
panic("index out of range for this inspector")
}
if events[index].index < index {
if in.events[index].index < index {
panic("invalid index") // (a push, not a pop)
}
return Cursor{in, index}
}
// Inspector returns the cursor's Inspector.
func (c Cursor) Inspector() *inspector.Inspector { return c.in }
func (c Cursor) Inspector() *Inspector { return c.in }
// Index returns the index of this cursor position within the package.
//
@ -83,7 +84,7 @@ func (c Cursor) Node() ast.Node {
if c.index < 0 {
return nil
}
return c.events()[c.index].node
return c.in.events[c.index].node
}
// String returns information about the cursor's node, if any.
@ -100,9 +101,9 @@ func (c Cursor) String() string {
// indices return the [start, end) half-open interval of event indices.
func (c Cursor) indices() (int32, int32) {
if c.index < 0 {
return 0, int32(len(c.events())) // root: all events
return 0, int32(len(c.in.events)) // root: all events
} else {
return c.index, c.events()[c.index].index + 1 // just one subtree
return c.index, c.in.events[c.index].index + 1 // just one subtree
}
}
@ -123,7 +124,7 @@ func (c Cursor) Preorder(types ...ast.Node) iter.Seq[Cursor] {
mask := maskOf(types)
return func(yield func(Cursor) bool) {
events := c.events()
events := c.in.events
for i, limit := c.indices(); i < limit; {
ev := events[i]
@ -158,7 +159,7 @@ func (c Cursor) Preorder(types ...ast.Node) iter.Seq[Cursor] {
// matches an element of the types slice.
func (c Cursor) Inspect(types []ast.Node, f func(c Cursor) (descend bool)) {
mask := maskOf(types)
events := c.events()
events := c.in.events
for i, limit := c.indices(); i < limit; {
ev := events[i]
if ev.index > i {
@ -193,7 +194,7 @@ func (c Cursor) Enclosing(types ...ast.Node) iter.Seq[Cursor] {
mask := maskOf(types)
return func(yield func(Cursor) bool) {
events := c.events()
events := c.in.events
for i := c.index; i >= 0; i = events[i].parent {
if events[i].typ&mask != 0 && !yield(Cursor{c.in, i}) {
break
@ -210,7 +211,7 @@ func (c Cursor) Parent() Cursor {
panic("Cursor.Parent called on Root node")
}
return Cursor{c.in, c.events()[c.index].parent}
return Cursor{c.in, c.in.events[c.index].parent}
}
// ParentEdge returns the identity of the field in the parent node
@ -227,7 +228,7 @@ func (c Cursor) ParentEdge() (edge.Kind, int) {
if c.index < 0 {
panic("Cursor.ParentEdge called on Root node")
}
events := c.events()
events := c.in.events
pop := events[c.index].index
return unpackEdgeKindAndIndex(events[pop].parent)
}
@ -244,7 +245,7 @@ func (c Cursor) ChildAt(k edge.Kind, idx int) Cursor {
target := packEdgeKindAndIndex(k, idx)
// Unfortunately there's no shortcut to looping.
events := c.events()
events := c.in.events
i := c.index + 1
for {
pop := events[i].index
@ -277,7 +278,7 @@ func (c Cursor) Child(n ast.Node) Cursor {
} else {
// optimized implementation
events := c.events()
events := c.in.events
for i := c.index + 1; events[i].index > i; i = events[i].index + 1 {
if events[i].node == n {
return Cursor{c.in, i}
@ -300,7 +301,7 @@ func (c Cursor) NextSibling() (Cursor, bool) {
panic("Cursor.NextSibling called on Root node")
}
events := c.events()
events := c.in.events
i := events[c.index].index + 1 // after corresponding pop
if i < int32(len(events)) {
if events[i].index > i { // push?
@ -323,7 +324,7 @@ func (c Cursor) PrevSibling() (Cursor, bool) {
panic("Cursor.PrevSibling called on Root node")
}
events := c.events()
events := c.in.events
i := c.index - 1
if i >= 0 {
if j := events[i].index; j < i { // pop?
@ -336,7 +337,7 @@ func (c Cursor) PrevSibling() (Cursor, bool) {
// FirstChild returns the first direct child of the current node,
// or zero if it has no children.
func (c Cursor) FirstChild() (Cursor, bool) {
events := c.events()
events := c.in.events
i := c.index + 1 // i=0 if c is root
if i < int32(len(events)) && events[i].index > i { // push?
return Cursor{c.in, i}, true
@ -347,7 +348,7 @@ func (c Cursor) FirstChild() (Cursor, bool) {
// LastChild returns the last direct child of the current node,
// or zero if it has no children.
func (c Cursor) LastChild() (Cursor, bool) {
events := c.events()
events := c.in.events
if c.index < 0 { // root?
if len(events) > 0 {
// return push of final event (a pop)
@ -406,7 +407,7 @@ func (c Cursor) Contains(c2 Cursor) bool {
if c.in != c2.in {
panic("different inspectors")
}
events := c.events()
events := c.in.events
return c.index <= c2.index && events[c2.index].index <= events[c.index].index
}
@ -430,7 +431,7 @@ func (c Cursor) FindNode(n ast.Node) (Cursor, bool) {
// like FindByPos?
mask := maskOf([]ast.Node{n})
events := c.events()
events := c.in.events
for i, limit := c.indices(); i < limit; i++ {
ev := events[i]
@ -461,7 +462,7 @@ func (c Cursor) FindByPos(start, end token.Pos) (Cursor, bool) {
if end < start {
panic("end < start")
}
events := c.events()
events := c.in.events
// This algorithm could be implemented using c.Inspect,
// but it is about 2.5x slower.

View file

@ -13,10 +13,19 @@
// This representation is sometimes called a "balanced parenthesis tree."
//
// Experiments suggest the inspector's traversals are about 2.5x faster
// than ast.Inspect, but it may take around 5 traversals for this
// than [ast.Inspect], but it may take around 5 traversals for this
// benefit to amortize the inspector's construction cost.
// If efficiency is the primary concern, do not use Inspector for
// one-off traversals.
//
// The [Cursor] type provides a more flexible API for efficient
// navigation of syntax trees in all four "cardinal directions". For
// example, traversals may be nested, so you can find each node of
// type A and then search within it for nodes of type B. Or you can
// traverse from a node to its immediate neighbors: its parent, its
// previous and next sibling, or its first and last child. We
// recommend using methods of Cursor in preference to Inspector where
// possible.
package inspector
// There are four orthogonal features in a traversal:
@ -37,9 +46,8 @@ package inspector
import (
"go/ast"
_ "unsafe"
"golang.org/x/tools/internal/astutil/edge"
"golang.org/x/tools/go/ast/edge"
)
// An Inspector provides methods for inspecting
@ -48,18 +56,12 @@ type Inspector struct {
events []event
}
//go:linkname events golang.org/x/tools/go/ast/inspector.events
func events(in *Inspector) []event { return in.events }
//go:linkname packEdgeKindAndIndex golang.org/x/tools/go/ast/inspector.packEdgeKindAndIndex
func packEdgeKindAndIndex(ek edge.Kind, index int) int32 {
return int32(uint32(index+1)<<7 | uint32(ek))
}
// unpackEdgeKindAndIndex unpacks the edge kind and edge index (within
// an []ast.Node slice) from the parent field of a pop event.
//
//go:linkname unpackEdgeKindAndIndex golang.org/x/tools/go/ast/inspector.unpackEdgeKindAndIndex
func unpackEdgeKindAndIndex(x int32) (edge.Kind, int) {
// The "parent" field of a pop node holds the
// edge Kind in the lower 7 bits and the index+1
@ -88,10 +90,15 @@ type event struct {
// depth-first order. It calls f(n) for each node n before it visits
// n's children.
//
// The complete traversal sequence is determined by ast.Inspect.
// The complete traversal sequence is determined by [ast.Inspect].
// The types argument, if non-empty, enables type-based filtering of
// events. The function f is called only for nodes whose type
// matches an element of the types slice.
//
// The [Cursor.Preorder] method provides a richer alternative interface.
// Example:
//
// for c := range in.Root().Preorder(types) { ... }
func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) {
// Because it avoids postorder calls to f, and the pruning
// check, Preorder is almost twice as fast as Nodes. The two
@ -131,10 +138,18 @@ func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) {
// of the non-nil children of the node, followed by a call of
// f(n, false).
//
// The complete traversal sequence is determined by ast.Inspect.
// The complete traversal sequence is determined by [ast.Inspect].
// The types argument, if non-empty, enables type-based filtering of
// events. The function f if is called only for nodes whose type
// matches an element of the types slice.
//
// The [Cursor.Inspect] method provides a richer alternative interface.
// Example:
//
// in.Root().Inspect(types, func(c Cursor) bool {
// ...
// return true
// }
func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proceed bool)) {
mask := maskOf(types)
for i := int32(0); i < int32(len(in.events)); {
@ -168,6 +183,15 @@ func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proc
// supplies each call to f an additional argument, the current
// traversal stack. The stack's first element is the outermost node,
// an *ast.File; its last is the innermost, n.
//
// The [Cursor.Inspect] method provides a richer alternative interface.
// Example:
//
// in.Root().Inspect(types, func(c Cursor) bool {
// stack := slices.Collect(c.Enclosing())
// ...
// return true
// })
func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, stack []ast.Node) (proceed bool)) {
mask := maskOf(types)
var stack []ast.Node
@ -233,7 +257,7 @@ type visitor struct {
type item struct {
index int32 // index of current node's push event
parentIndex int32 // index of parent node's push event
typAccum uint64 // accumulated type bits of current node's descendents
typAccum uint64 // accumulated type bits of current node's descendants
edgeKindAndIndex int32 // edge.Kind and index, bit packed
}

View file

@ -13,7 +13,7 @@ import (
"fmt"
"go/ast"
"golang.org/x/tools/internal/astutil/edge"
"golang.org/x/tools/go/ast/edge"
)
func walkList[N ast.Node](v *visitor, ek edge.Kind, list []N) {

View file

@ -603,7 +603,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) {
type hasTypeParams interface {
TypeParams() *types.TypeParamList
}
// abstraction of *types.{Named,TypeParam}
// abstraction of *types.{Alias,Named,TypeParam}
type hasObj interface {
Obj() *types.TypeName
}

View file

@ -22,7 +22,6 @@ import (
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/internal/astutil/cursor"
"golang.org/x/tools/internal/typesinternal"
)
@ -526,7 +525,7 @@ func CanImport(from, to string) bool {
func DeleteStmt(fset *token.FileSet, astFile *ast.File, stmt ast.Stmt, report func(string, ...any)) []analysis.TextEdit {
// TODO: pass in the cursor to a ast.Stmt. callers should provide the Cursor
insp := inspector.New([]*ast.File{astFile})
root := cursor.Root(insp)
root := insp.Root()
cstmt, ok := root.FindNode(stmt)
if !ok {
report("%s not found in file", stmt.Pos())
@ -620,8 +619,8 @@ Outer:
// otherwise remove the line
edit := analysis.TextEdit{Pos: stmt.Pos(), End: stmt.End()}
if from.IsValid() || to.IsValid() {
// remove just the statment.
// we can't tell if there is a ; or whitespace right after the statment
// remove just the statement.
// we can't tell if there is a ; or whitespace right after the statement
// ideally we'd like to remove the former and leave the latter
// (if gofmt has run, there likely won't be a ;)
// In type switches we know there's a semicolon somewhere after the statement,
@ -671,3 +670,14 @@ func IsStdPackage(path string) bool {
}
return !strings.Contains(path[:slash], ".") && path != "testdata"
}
// Range returns an [analysis.Range] for the specified start and end positions.
func Range(pos, end token.Pos) analysis.Range {
return tokenRange{pos, end}
}
// tokenRange is an implementation of the [analysis.Range] interface.
type tokenRange struct{ StartPos, EndPos token.Pos }
func (r tokenRange) Pos() token.Pos { return r.StartPos }
func (r tokenRange) End() token.Pos { return r.EndPos }

View file

@ -1,40 +0,0 @@
// Copyright 2024 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.
//go:build go1.23
package cursor
import (
"go/ast"
_ "unsafe" // for go:linkname
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/internal/astutil/edge"
)
// This file defines backdoor access to inspector.
// Copied from inspector.event; must remain in sync.
// (Note that the linkname effects a type coercion too.)
type event struct {
node ast.Node
typ uint64 // typeOf(node) on push event, or union of typ strictly between push and pop events on pop events
index int32 // index of corresponding push or pop event (relative to this event's index, +ve=push, -ve=pop)
parent int32 // index of parent's push node (push nodes only); or edge and index, bit packed (pop nodes only)
}
//go:linkname maskOf golang.org/x/tools/go/ast/inspector.maskOf
func maskOf(nodes []ast.Node) uint64
//go:linkname events golang.org/x/tools/go/ast/inspector.events
func events(in *inspector.Inspector) []event
//go:linkname packEdgeKindAndIndex golang.org/x/tools/go/ast/inspector.packEdgeKindAndIndex
func packEdgeKindAndIndex(edge.Kind, int) int32
//go:linkname unpackEdgeKindAndIndex golang.org/x/tools/go/ast/inspector.unpackEdgeKindAndIndex
func unpackEdgeKindAndIndex(int32) (edge.Kind, int)
func (c Cursor) events() []event { return events(c.in) }

View file

@ -71,6 +71,8 @@ func PosInStringLiteral(lit *ast.BasicLit, offset int) (token.Pos, error) {
// In practice, the second call is nearly always used only to pop the
// stack, and it is surprisingly tricky to do this correctly; see
// https://go.dev/issue/73319.
//
// TODO(adonovan): replace with [ast.PreorderStack] when go1.25 is assured.
func PreorderStack(root ast.Node, stack []ast.Node, f func(n ast.Node, stack []ast.Node) bool) {
before := len(stack)
ast.Inspect(root, func(n ast.Node) bool {

View file

@ -52,7 +52,7 @@ func importMap(imports []*types.Package) map[string]*types.Package {
// nop
case typesinternal.NamedOrAlias: // *types.{Named,Alias}
// Add the type arguments if this is an instance.
if targs := typesinternal.TypeArgs(T); targs.Len() > 0 {
if targs := T.TypeArgs(); targs.Len() > 0 {
for i := 0; i < targs.Len(); i++ {
addType(targs.At(i))
}
@ -69,7 +69,7 @@ func importMap(imports []*types.Package) map[string]*types.Package {
// common aspects
addObj(T.Obj())
if tparams := typesinternal.TypeParams(T); tparams.Len() > 0 {
if tparams := T.TypeParams(); tparams.Len() > 0 {
for i := 0; i < tparams.Len(); i++ {
addType(tparams.At(i))
}

View file

@ -12,61 +12,61 @@ type pkginfo struct {
}
var deps = [...]pkginfo{
{"archive/tar", "\x03j\x03E6\x01\v\x01\"\x01\x01\x02\x05\n\x02\x01\x02\x02\v"},
{"archive/zip", "\x02\x04`\a\x16\x0206\x01*\x05\x01\x11\x03\x02\r\x04"},
{"bufio", "\x03j~E\x13"},
{"bytes", "m+S\x03\fG\x02\x02"},
{"archive/tar", "\x03j\x03E5\x01\v\x01#\x01\x01\x02\x05\n\x02\x01\x02\x02\v"},
{"archive/zip", "\x02\x04`\a\x16\x0205\x01+\x05\x01\x11\x03\x02\r\x04"},
{"bufio", "\x03j}F\x13"},
{"bytes", "m+R\x03\fH\x02\x02"},
{"cmp", ""},
{"compress/bzip2", "\x02\x02\xe7\x01B"},
{"compress/flate", "\x02k\x03{\r\x024\x01\x03"},
{"compress/gzip", "\x02\x04`\a\x03\x15fT"},
{"compress/lzw", "\x02k\x03{"},
{"compress/zlib", "\x02\x04`\a\x03\x13\x01g"},
{"compress/bzip2", "\x02\x02\xe6\x01C"},
{"compress/flate", "\x02k\x03z\r\x025\x01\x03"},
{"compress/gzip", "\x02\x04`\a\x03\x15eU"},
{"compress/lzw", "\x02k\x03z"},
{"compress/zlib", "\x02\x04`\a\x03\x13\x01f"},
{"container/heap", "\xae\x02"},
{"container/list", ""},
{"container/ring", ""},
{"context", "m\\i\x01\f"},
{"crypto", "\x83\x01hD"},
{"crypto", "\x83\x01gE"},
{"crypto/aes", "\x10\n\a\x8e\x02"},
{"crypto/cipher", "\x03\x1e\x01\x01\x1d\x11\x1c,R"},
{"crypto/cipher", "\x03\x1e\x01\x01\x1d\x11\x1c,Q"},
{"crypto/des", "\x10\x13\x1d-,\x96\x01\x03"},
{"crypto/dsa", "@\x04)~\x0e"},
{"crypto/ecdh", "\x03\v\f\x0e\x04\x14\x04\r\x1c~"},
{"crypto/ecdsa", "\x0e\x05\x03\x04\x01\x0e\x16\x01\x04\f\x01\x1c~\x0e\x04K\x01"},
{"crypto/ed25519", "\x0e\x1c\x16\n\a\x1c~D"},
{"crypto/elliptic", "0=~\x0e9"},
{"crypto/dsa", "@\x04)}\x0e"},
{"crypto/ecdh", "\x03\v\f\x0e\x04\x14\x04\r\x1c}"},
{"crypto/ecdsa", "\x0e\x05\x03\x04\x01\x0e\x16\x01\x04\f\x01\x1c}\x0e\x04L\x01"},
{"crypto/ed25519", "\x0e\x1c\x16\n\a\x1c}E"},
{"crypto/elliptic", "0=}\x0e:"},
{"crypto/fips140", " \x05\x90\x01"},
{"crypto/hkdf", "-\x12\x01-\x16"},
{"crypto/hmac", "\x1a\x14\x11\x01\x112"},
{"crypto/internal/boring", "\x0e\x02\rf"},
{"crypto/internal/boring/bbig", "\x1a\xdf\x01L"},
{"crypto/internal/boring/bbig", "\x1a\xde\x01M"},
{"crypto/internal/boring/bcache", "\xb3\x02\x12"},
{"crypto/internal/boring/sig", ""},
{"crypto/internal/cryptotest", "\x03\r\n)\x0e\x19\x06\x13\x12#\a\t\x11\x12\x11\x1a\r\r\x05\n"},
{"crypto/internal/cryptotest", "\x03\r\n)\x0e\x19\x06\x13\x12#\a\t\x11\x11\x11\x1b\x01\f\r\x05\n"},
{"crypto/internal/entropy", "E"},
{"crypto/internal/fips140", ">/~8\r\x15"},
{"crypto/internal/fips140/aes", "\x03\x1d\x03\x02\x13\x04\x01\x01\x05*\x8d\x015"},
{"crypto/internal/fips140/aes/gcm", " \x01\x02\x02\x02\x11\x04\x01\x06*\x8b\x01"},
{"crypto/internal/fips140", ">/}9\r\x15"},
{"crypto/internal/fips140/aes", "\x03\x1d\x03\x02\x13\x04\x01\x01\x05*\x8c\x016"},
{"crypto/internal/fips140/aes/gcm", " \x01\x02\x02\x02\x11\x04\x01\x06*\x8a\x01"},
{"crypto/internal/fips140/alias", "\xc5\x02"},
{"crypto/internal/fips140/bigmod", "%\x17\x01\x06*\x8d\x01"},
{"crypto/internal/fips140/check", " \x0e\x06\b\x02\xad\x01Z"},
{"crypto/internal/fips140/bigmod", "%\x17\x01\x06*\x8c\x01"},
{"crypto/internal/fips140/check", " \x0e\x06\b\x02\xac\x01["},
{"crypto/internal/fips140/check/checktest", "%\xfe\x01\""},
{"crypto/internal/fips140/drbg", "\x03\x1c\x01\x01\x04\x13\x04\b\x01(~\x0f8"},
{"crypto/internal/fips140/ecdh", "\x03\x1d\x05\x02\t\f1~\x0f8"},
{"crypto/internal/fips140/ecdsa", "\x03\x1d\x04\x01\x02\a\x02\x067~G"},
{"crypto/internal/fips140/drbg", "\x03\x1c\x01\x01\x04\x13\x04\b\x01(}\x0f9"},
{"crypto/internal/fips140/ecdh", "\x03\x1d\x05\x02\t\f1}\x0f9"},
{"crypto/internal/fips140/ecdsa", "\x03\x1d\x04\x01\x02\a\x02\x067}H"},
{"crypto/internal/fips140/ed25519", "\x03\x1d\x05\x02\x04\v7\xc2\x01\x03"},
{"crypto/internal/fips140/edwards25519", "%\a\f\x041\x8d\x018"},
{"crypto/internal/fips140/edwards25519/field", "%\x13\x041\x8d\x01"},
{"crypto/internal/fips140/edwards25519", "%\a\f\x041\x8c\x019"},
{"crypto/internal/fips140/edwards25519/field", "%\x13\x041\x8c\x01"},
{"crypto/internal/fips140/hkdf", "\x03\x1d\x05\t\x069"},
{"crypto/internal/fips140/hmac", "\x03\x1d\x14\x01\x017"},
{"crypto/internal/fips140/mlkem", "\x03\x1d\x05\x02\x0e\x03\x041"},
{"crypto/internal/fips140/nistec", "%\f\a\x041\x8d\x01)\x0f\x13"},
{"crypto/internal/fips140/nistec/fiat", "%\x135\x8d\x01"},
{"crypto/internal/fips140/nistec", "%\f\a\x041\x8c\x01*\x0f\x13"},
{"crypto/internal/fips140/nistec/fiat", "%\x135\x8c\x01"},
{"crypto/internal/fips140/pbkdf2", "\x03\x1d\x05\t\x069"},
{"crypto/internal/fips140/rsa", "\x03\x1d\x04\x01\x02\r\x01\x01\x025~G"},
{"crypto/internal/fips140/sha256", "\x03\x1d\x1c\x01\x06*\x8d\x01"},
{"crypto/internal/fips140/sha3", "\x03\x1d\x18\x04\x010\x8d\x01K"},
{"crypto/internal/fips140/sha512", "\x03\x1d\x1c\x01\x06*\x8d\x01"},
{"crypto/internal/fips140/rsa", "\x03\x1d\x04\x01\x02\r\x01\x01\x025}H"},
{"crypto/internal/fips140/sha256", "\x03\x1d\x1c\x01\x06*\x8c\x01"},
{"crypto/internal/fips140/sha3", "\x03\x1d\x18\x04\x010\x8c\x01L"},
{"crypto/internal/fips140/sha512", "\x03\x1d\x1c\x01\x06*\x8c\x01"},
{"crypto/internal/fips140/ssh", " \x05"},
{"crypto/internal/fips140/subtle", "#"},
{"crypto/internal/fips140/tls12", "\x03\x1d\x05\t\x06\x027"},
@ -76,90 +76,90 @@ var deps = [...]pkginfo{
{"crypto/internal/fips140deps/cpu", "\xad\x01\a"},
{"crypto/internal/fips140deps/godebug", "\xb5\x01"},
{"crypto/internal/fips140hash", "5\x1a4\xc2\x01"},
{"crypto/internal/fips140only", "'\r\x01\x01M26"},
{"crypto/internal/fips140only", "'\r\x01\x01M25"},
{"crypto/internal/fips140test", ""},
{"crypto/internal/hpke", "\x0e\x01\x01\x03\x1a\x1d#,aM"},
{"crypto/internal/hpke", "\x0e\x01\x01\x03\x1a\x1d#,`N"},
{"crypto/internal/impl", "\xb0\x02"},
{"crypto/internal/randutil", "\xeb\x01\x12"},
{"crypto/internal/sysrand", "mi\"\x1e\r\x0f\x01\x01\v\x06"},
{"crypto/internal/randutil", "\xea\x01\x12"},
{"crypto/internal/sysrand", "mi!\x1f\r\x0f\x01\x01\v\x06"},
{"crypto/internal/sysrand/internal/seccomp", "m"},
{"crypto/md5", "\x0e2-\x16\x16a"},
{"crypto/md5", "\x0e2-\x16\x16`"},
{"crypto/mlkem", "/"},
{"crypto/pbkdf2", "2\r\x01-\x16"},
{"crypto/rand", "\x1a\x06\a\x19\x04\x01(~\x0eL"},
{"crypto/rand", "\x1a\x06\a\x19\x04\x01(}\x0eM"},
{"crypto/rc4", "#\x1d-\xc2\x01"},
{"crypto/rsa", "\x0e\f\x01\t\x0f\f\x01\x04\x06\a\x1c\x03\x1326\r\x01"},
{"crypto/sha1", "\x0e\f&-\x16\x16\x14M"},
{"crypto/rsa", "\x0e\f\x01\t\x0f\f\x01\x04\x06\a\x1c\x03\x1325\r\x01"},
{"crypto/sha1", "\x0e\f&-\x16\x16\x14L"},
{"crypto/sha256", "\x0e\f\x1aO"},
{"crypto/sha3", "\x0e'N\xc2\x01"},
{"crypto/sha512", "\x0e\f\x1cM"},
{"crypto/subtle", "8\x96\x01U"},
{"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x03\x01\a\x01\v\x02\n\x01\b\x05\x03\x01\x01\x01\x01\x02\x01\x02\x01\x17\x02\x03\x13\x16\x14\b6\x16\x15\r\n\x01\x01\x01\x02\x01\f\x06\x02\x01"},
{"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x03\x01\a\x01\v\x02\n\x01\b\x05\x03\x01\x01\x01\x01\x02\x01\x02\x01\x17\x02\x03\x13\x16\x14\b5\x16\x16\r\n\x01\x01\x01\x02\x01\f\x06\x02\x01"},
{"crypto/tls/internal/fips140tls", " \x93\x02"},
{"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x011\x03\x02\x01\x01\x02\x05\x0e\x06\x02\x02\x03E\x033\x01\x02\t\x01\x01\x01\a\x0f\x05\x01\x06\x02\x05\f\x01\x02\r\x02\x01\x01\x02\x03\x01"},
{"crypto/x509/pkix", "c\x06\a\x89\x01F"},
{"database/sql", "\x03\nJ\x16\x03{\f\x06!\x05\n\x02\x03\x01\f\x02\x02\x02"},
{"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x011\x03\x02\x01\x01\x02\x05\x0e\x06\x02\x02\x03E\x032\x01\x02\t\x01\x01\x01\a\x10\x05\x01\x06\x02\x05\f\x01\x02\r\x02\x01\x01\x02\x03\x01"},
{"crypto/x509/pkix", "c\x06\a\x88\x01G"},
{"database/sql", "\x03\nJ\x16\x03z\f\x06\"\x05\n\x02\x03\x01\f\x02\x02\x02"},
{"database/sql/driver", "\r`\x03\xae\x01\x11\x10"},
{"debug/buildinfo", "\x03W\x02\x01\x01\b\a\x03`\x19\x02\x01*\x0f "},
{"debug/dwarf", "\x03c\a\x03{0\x13\x01\x01"},
{"debug/elf", "\x03\x06P\r\a\x03`\x1a\x01+\x19\x01\x15"},
{"debug/buildinfo", "\x03W\x02\x01\x01\b\a\x03`\x18\x02\x01+\x0f "},
{"debug/dwarf", "\x03c\a\x03z1\x13\x01\x01"},
{"debug/elf", "\x03\x06P\r\a\x03`\x19\x01,\x19\x01\x15"},
{"debug/gosym", "\x03c\n\xbe\x01\x01\x01\x02"},
{"debug/macho", "\x03\x06P\r\n`\x1b+\x19\x01"},
{"debug/pe", "\x03\x06P\r\a\x03`\x1b+\x19\x01\x15"},
{"debug/plan9obj", "f\a\x03`\x1b+"},
{"embed", "m+:\x19\x01S"},
{"debug/macho", "\x03\x06P\r\n`\x1a,\x19\x01"},
{"debug/pe", "\x03\x06P\r\a\x03`\x1a,\x19\x01\x15"},
{"debug/plan9obj", "f\a\x03`\x1a,"},
{"embed", "m+:\x18\x01T"},
{"embed/internal/embedtest", ""},
{"encoding", ""},
{"encoding/ascii85", "\xeb\x01D"},
{"encoding/asn1", "\x03j\x03\x88\x01\x01%\x0f\x02\x01\x0f\x03\x01"},
{"encoding/base32", "\xeb\x01B\x02"},
{"encoding/base64", "f\x85\x01B\x02"},
{"encoding/binary", "m~\r&\x0f\x05"},
{"encoding/csv", "\x02\x01j\x03{E\x11\x02"},
{"encoding/gob", "\x02_\x05\a\x03`\x1b\f\x01\x02\x1c\b\x14\x01\x0e\x02"},
{"encoding/hex", "m\x03{B\x03"},
{"encoding/json", "\x03\x01]\x04\b\x03{\r&\x0f\x02\x01\x02\x0f\x01\x01\x02"},
{"encoding/pem", "\x03b\b~B\x03"},
{"encoding/xml", "\x02\x01^\f\x03{3\x05\f\x01\x02\x0f\x02"},
{"encoding/ascii85", "\xea\x01E"},
{"encoding/asn1", "\x03j\x03\x87\x01\x01&\x0f\x02\x01\x0f\x03\x01"},
{"encoding/base32", "\xea\x01C\x02"},
{"encoding/base64", "\x99\x01QC\x02"},
{"encoding/binary", "m}\r'\x0f\x05"},
{"encoding/csv", "\x02\x01j\x03zF\x11\x02"},
{"encoding/gob", "\x02_\x05\a\x03`\x1a\f\x01\x02\x1d\b\x14\x01\x0e\x02"},
{"encoding/hex", "m\x03zC\x03"},
{"encoding/json", "\x03\x01]\x04\b\x03z\r'\x0f\x02\x01\x02\x0f\x01\x01\x02"},
{"encoding/pem", "\x03b\b}C\x03"},
{"encoding/xml", "\x02\x01^\f\x03z4\x05\f\x01\x02\x0f\x02"},
{"errors", "\xc9\x01|"},
{"expvar", "jK:\t\n\x14\r\n\x02\x03\x01\x10"},
{"flag", "a\f\x03{+\b\x05\n\x02\x01\x0f"},
{"fmt", "mE9\r\x1e\b\x0f\x02\x03\x11"},
{"go/ast", "\x03\x01l\x0f\x01k\x03(\b\x0f\x02\x01"},
{"expvar", "jK9\t\n\x15\r\n\x02\x03\x01\x10"},
{"flag", "a\f\x03z,\b\x05\n\x02\x01\x0f"},
{"fmt", "mE8\r\x1f\b\x0f\x02\x03\x11"},
{"go/ast", "\x03\x01l\x0f\x01j\x03)\b\x0f\x02\x01"},
{"go/ast/internal/tests", ""},
{"go/build", "\x02\x01j\x03\x01\x03\x02\a\x02\x01\x17\x1e\x04\x02\t\x14\x13\x01*\x01\x04\x01\a\n\x02\x01\x11\x02\x02"},
{"go/build", "\x02\x01j\x03\x01\x03\x02\a\x02\x01\x17\x1e\x04\x02\t\x14\x12\x01+\x01\x04\x01\a\n\x02\x01\x11\x02\x02"},
{"go/build/constraint", "m\xc2\x01\x01\x11\x02"},
{"go/constant", "p\x10x\x01\x015\x01\x02\x11"},
{"go/doc", "\x04l\x01\x06\t=.0\x12\x02\x01\x11\x02"},
{"go/constant", "p\x10w\x01\x016\x01\x02\x11"},
{"go/doc", "\x04l\x01\x06\t=-1\x12\x02\x01\x11\x02"},
{"go/doc/comment", "\x03m\xbd\x01\x01\x01\x01\x11\x02"},
{"go/format", "\x03m\x01\f\x01\x02kE"},
{"go/importer", "s\a\x01\x01\x04\x01j8"},
{"go/internal/gccgoimporter", "\x02\x01W\x13\x03\x05\v\x01h\x02+\x01\x05\x13\x01\v\b"},
{"go/internal/gcimporter", "\x02n\x10\x01/\x05\x0e(+\x17\x03\x02"},
{"go/internal/srcimporter", "p\x01\x02\n\x03\x01j+\x01\x05\x14\x02\x13"},
{"go/parser", "\x03j\x03\x01\x03\v\x01k\x01*\x06\x14"},
{"go/printer", "p\x01\x03\x03\tk\r\x1e\x17\x02\x01\x02\n\x05\x02"},
{"go/scanner", "\x03m\x10k1\x12\x01\x12\x02"},
{"go/format", "\x03m\x01\f\x01\x02jF"},
{"go/importer", "s\a\x01\x01\x04\x01i9"},
{"go/internal/gccgoimporter", "\x02\x01W\x13\x03\x05\v\x01g\x02,\x01\x05\x13\x01\v\b"},
{"go/internal/gcimporter", "\x02n\x10\x01/\x05\x0e',\x17\x03\x02"},
{"go/internal/srcimporter", "p\x01\x02\n\x03\x01i,\x01\x05\x14\x02\x13"},
{"go/parser", "\x03j\x03\x01\x03\v\x01j\x01+\x06\x14"},
{"go/printer", "p\x01\x03\x03\tj\r\x1f\x17\x02\x01\x02\n\x05\x02"},
{"go/scanner", "\x03m\x10j2\x12\x01\x12\x02"},
{"go/token", "\x04l\xbd\x01\x02\x03\x01\x0e\x02"},
{"go/types", "\x03\x01\x06c\x03\x01\x04\b\x03\x02\x15\x1e\x06,\x04\x03\n$\a\n\x01\x01\x01\x02\x01\x0e\x02\x02"},
{"go/types", "\x03\x01\x06c\x03\x01\x04\b\x03\x02\x15\x1e\x06+\x04\x03\n%\a\n\x01\x01\x01\x02\x01\x0e\x02\x02"},
{"go/version", "\xba\x01v"},
{"hash", "\xeb\x01"},
{"hash", "\xea\x01"},
{"hash/adler32", "m\x16\x16"},
{"hash/crc32", "m\x16\x16\x14\x85\x01\x01\x12"},
{"hash/crc64", "m\x16\x16\x99\x01"},
{"hash/fnv", "m\x16\x16a"},
{"hash/maphash", "\x94\x01\x05\x1b\x03AM"},
{"hash/fnv", "m\x16\x16`"},
{"hash/maphash", "\x94\x01\x05\x1b\x03@N"},
{"html", "\xb0\x02\x02\x11"},
{"html/template", "\x03g\x06\x19,6\x01\v\x1f\x05\x01\x02\x03\x0e\x01\x02\v\x01\x03\x02"},
{"image", "\x02k\x1f_\x0f5\x03\x01"},
{"html/template", "\x03g\x06\x19,5\x01\v \x05\x01\x02\x03\x0e\x01\x02\v\x01\x03\x02"},
{"image", "\x02k\x1f^\x0f6\x03\x01"},
{"image/color", ""},
{"image/color/palette", "\x8c\x01"},
{"image/draw", "\x8b\x01\x01\x04"},
{"image/gif", "\x02\x01\x05e\x03\x1b\x01\x01\x01\vR"},
{"image/gif", "\x02\x01\x05e\x03\x1b\x01\x01\x01\vQ"},
{"image/internal/imageutil", "\x8b\x01"},
{"image/jpeg", "\x02k\x1e\x01\x04["},
{"image/png", "\x02\a]\n\x13\x02\x06\x01_D"},
{"index/suffixarray", "\x03c\a~\r)\f\x01"},
{"image/jpeg", "\x02k\x1e\x01\x04Z"},
{"image/png", "\x02\a]\n\x13\x02\x06\x01^E"},
{"index/suffixarray", "\x03c\a}\r*\f\x01"},
{"internal/abi", "\xb4\x01\x91\x01"},
{"internal/asan", "\xc5\x02"},
{"internal/bisect", "\xa3\x02\x0f\x01"},
@ -171,27 +171,27 @@ var deps = [...]pkginfo{
{"internal/copyright", ""},
{"internal/coverage", ""},
{"internal/coverage/calloc", ""},
{"internal/coverage/cfile", "j\x06\x17\x16\x01\x02\x01\x01\x01\x01\x01\x01\x01#\x01 +\x06\a\f\x01\x03\f\x06"},
{"internal/coverage/cformat", "\x04l-\x04J\f6\x01\x02\f"},
{"internal/coverage/cmerge", "p-["},
{"internal/coverage/decodecounter", "f\n-\v\x02A+\x19\x16"},
{"internal/coverage/decodemeta", "\x02d\n\x17\x16\v\x02A+"},
{"internal/coverage/encodecounter", "\x02d\n-\f\x01\x02?\f\x1f\x17"},
{"internal/coverage/encodemeta", "\x02\x01c\n\x13\x04\x16\r\x02?+/"},
{"internal/coverage/cfile", "j\x06\x17\x16\x01\x02\x01\x01\x01\x01\x01\x01\x01#\x01\x1f,\x06\a\f\x01\x03\f\x06"},
{"internal/coverage/cformat", "\x04l-\x04I\f7\x01\x02\f"},
{"internal/coverage/cmerge", "p-Z"},
{"internal/coverage/decodecounter", "f\n-\v\x02@,\x19\x16"},
{"internal/coverage/decodemeta", "\x02d\n\x17\x16\v\x02@,"},
{"internal/coverage/encodecounter", "\x02d\n-\f\x01\x02>\f \x17"},
{"internal/coverage/encodemeta", "\x02\x01c\n\x13\x04\x16\r\x02>,/"},
{"internal/coverage/pods", "\x04l-y\x06\x05\f\x02\x01"},
{"internal/coverage/rtcov", "\xc5\x02"},
{"internal/coverage/slicereader", "f\n{Z"},
{"internal/coverage/slicewriter", "p{"},
{"internal/coverage/stringtab", "p8\x04?"},
{"internal/coverage/slicereader", "f\nz["},
{"internal/coverage/slicewriter", "pz"},
{"internal/coverage/stringtab", "p8\x04>"},
{"internal/coverage/test", ""},
{"internal/coverage/uleb128", ""},
{"internal/cpu", "\xc5\x02"},
{"internal/dag", "\x04l\xbd\x01\x03"},
{"internal/diff", "\x03m\xbe\x01\x02"},
{"internal/exportdata", "\x02\x01j\x03\x03]\x1b+\x01\x05\x13\x01\x02"},
{"internal/filepathlite", "m+:\x1aA"},
{"internal/exportdata", "\x02\x01j\x03\x03]\x1a,\x01\x05\x13\x01\x02"},
{"internal/filepathlite", "m+:\x19B"},
{"internal/fmtsort", "\x04\x9a\x02\x0f"},
{"internal/fuzz", "\x03\nA\x18\x04\x03\x03\x01\f\x0356\r\x02\x1c\x01\x05\x02\x05\f\x01\x02\x01\x01\v\x04\x02"},
{"internal/fuzz", "\x03\nA\x18\x04\x03\x03\x01\f\x0355\r\x02\x1d\x01\x05\x02\x05\f\x01\x02\x01\x01\v\x04\x02"},
{"internal/goarch", ""},
{"internal/godebug", "\x96\x01 |\x01\x12"},
{"internal/godebugs", ""},
@ -202,158 +202,158 @@ var deps = [...]pkginfo{
{"internal/goversion", ""},
{"internal/itoa", ""},
{"internal/lazyregexp", "\x96\x02\v\x0f\x02"},
{"internal/lazytemplate", "\xeb\x01+\x1a\x02\v"},
{"internal/lazytemplate", "\xea\x01,\x1a\x02\v"},
{"internal/msan", "\xc5\x02"},
{"internal/nettrace", ""},
{"internal/obscuretestdata", "e\x86\x01+"},
{"internal/obscuretestdata", "e\x85\x01,"},
{"internal/oserror", "m"},
{"internal/pkgbits", "\x03K\x18\a\x03\x05\vk\x0e\x1d\r\f\x01"},
{"internal/pkgbits", "\x03K\x18\a\x03\x05\vj\x0e\x1e\r\f\x01"},
{"internal/platform", ""},
{"internal/poll", "mO\x1a\x158\x0f\x01\x01\v\x06"},
{"internal/profile", "\x03\x04f\x03{6\r\x01\x01\x0f"},
{"internal/poll", "mO\x1a\x149\x0f\x01\x01\v\x06"},
{"internal/profile", "\x03\x04f\x03z7\r\x01\x01\x0f"},
{"internal/profilerecord", ""},
{"internal/race", "\x94\x01\xb1\x01"},
{"internal/reflectlite", "\x94\x01 4;\""},
{"internal/reflectlite", "\x94\x01 3<\""},
{"internal/runtime/atomic", "\xc5\x02"},
{"internal/runtime/exithook", "\xca\x01{"},
{"internal/runtime/maps", "\x94\x01\x01\x1f\v\t\x05\x01w"},
{"internal/runtime/math", "\xb4\x01"},
{"internal/runtime/sys", "\xb4\x01\x04"},
{"internal/runtime/syscall", "\xc5\x02"},
{"internal/saferio", "\xeb\x01Z"},
{"internal/saferio", "\xea\x01["},
{"internal/singleflight", "\xb2\x02"},
{"internal/stringslite", "\x98\x01\xad\x01"},
{"internal/sync", "\x94\x01 \x14k\x12"},
{"internal/synctest", "\xc5\x02"},
{"internal/syscall/execenv", "\xb4\x02"},
{"internal/syscall/unix", "\xa3\x02\x10\x01\x11"},
{"internal/sysinfo", "\x02\x01\xaa\x01>+\x1a\x02"},
{"internal/sysinfo", "\x02\x01\xaa\x01=,\x1a\x02"},
{"internal/syslist", ""},
{"internal/testenv", "\x03\n`\x02\x01*\x1a\x10(*\x01\x05\a\f\x01\x02\x02\x01\n"},
{"internal/testenv", "\x03\n`\x02\x01*\x1a\x10'+\x01\x05\a\f\x01\x02\x02\x01\n"},
{"internal/testlog", "\xb2\x02\x01\x12"},
{"internal/testpty", "m\x03\xa6\x01"},
{"internal/trace", "\x02\x01\x01\x06\\\a\x03m\x01\x01\x06\x06\x03\n5\x01\x02\x0f"},
{"internal/trace/event", ""},
{"internal/trace/event/go122", "pm"},
{"internal/trace/internal/oldtrace", "\x03\x01b\a\x03m\b\x06\r5\x01"},
{"internal/trace/internal/testgen/go122", "\x03c\nl\x01\x01\x03\x04\x010\v\x0f"},
{"internal/trace/raw", "\x02d\nm\b\x06D\x01\x11"},
{"internal/trace/testtrace", "\x02\x01j\x03l\x05\x05\x056\f\x02\x01"},
{"internal/trace/traceviewer", "\x02]\v\x06\x1a<\x16\b\a\x04\t\n\x14\x01\x05\a\f\x01\x02\r"},
{"internal/trace", "\x02\x01\x01\x06\\\a\x03n\x03\x03\x06\x03\n6\x01\x02\x0f\x06"},
{"internal/trace/internal/testgen", "\x03c\nl\x03\x02\x03\x011\v\x0f"},
{"internal/trace/internal/tracev1", "\x03\x01b\a\x03t\x06\r6\x01"},
{"internal/trace/raw", "\x02d\nq\x03\x06E\x01\x11"},
{"internal/trace/testtrace", "\x02\x01j\x03l\x03\x06\x057\f\x02\x01"},
{"internal/trace/tracev2", ""},
{"internal/trace/traceviewer", "\x02]\v\x06\x1a<\x16\a\a\x04\t\n\x15\x01\x05\a\f\x01\x02\r"},
{"internal/trace/traceviewer/format", ""},
{"internal/trace/version", "pm\x01\r"},
{"internal/trace/version", "pq\t"},
{"internal/txtar", "\x03m\xa6\x01\x1a"},
{"internal/types/errors", "\xaf\x02"},
{"internal/unsafeheader", "\xc5\x02"},
{"internal/xcoff", "Y\r\a\x03`\x1b+\x19\x01"},
{"internal/zstd", "f\a\x03{\x0f"},
{"internal/xcoff", "Y\r\a\x03`\x1a,\x19\x01"},
{"internal/zstd", "f\a\x03z\x0f"},
{"io", "m\xc5\x01"},
{"io/fs", "m+*)0\x12\x12\x04"},
{"io/ioutil", "\xeb\x01\x01*\x17\x03"},
{"io/fs", "m+*(1\x12\x12\x04"},
{"io/ioutil", "\xea\x01\x01+\x17\x03"},
{"iter", "\xc8\x01[\""},
{"log", "p{\x05&\r\x0f\x01\f"},
{"log", "pz\x05'\r\x0f\x01\f"},
{"log/internal", ""},
{"log/slog", "\x03\nT\t\x03\x03{\x04\x01\x02\x02\x04&\x05\n\x02\x01\x02\x01\f\x02\x02\x02"},
{"log/slog", "\x03\nT\t\x03\x03z\x04\x01\x02\x02\x04'\x05\n\x02\x01\x02\x01\f\x02\x02\x02"},
{"log/slog/internal", ""},
{"log/slog/internal/benchmarks", "\r`\x03{\x06\x03;\x10"},
{"log/slog/internal/benchmarks", "\r`\x03z\x06\x03<\x10"},
{"log/slog/internal/buffer", "\xb2\x02"},
{"log/slog/internal/slogtest", "\xf1\x01"},
{"log/syslog", "m\x03\x7f\x12\x15\x1a\x02\r"},
{"maps", "\xee\x01W"},
{"math", "\xad\x01MK"},
{"math/big", "\x03j\x03)\x14>\r\x02\x023\x01\x02\x13"},
{"log/slog/internal/slogtest", "\xf0\x01"},
{"log/syslog", "m\x03~\x12\x16\x1a\x02\r"},
{"maps", "\xed\x01X"},
{"math", "\xad\x01LL"},
{"math/big", "\x03j\x03)\x14=\r\x02\x024\x01\x02\x13"},
{"math/bits", "\xc5\x02"},
{"math/cmplx", "\xf8\x01\x02"},
{"math/rand", "\xb5\x01C:\x01\x12"},
{"math/rand/v2", "m,\x02]\x02K"},
{"mime", "\x02\x01b\b\x03{\f\x1f\x17\x03\x02\x0f\x02"},
{"mime/multipart", "\x02\x01G#\x03E6\f\x01\x06\x02\x14\x02\x06\x11\x02\x01\x15"},
{"mime/quotedprintable", "\x02\x01m{"},
{"net", "\x04\t`+\x1d\a\x04\x05\f\x01\x04\x15\x01$\x06\r\n\x05\x01\x01\v\x06\a"},
{"net/http", "\x02\x01\x04\x04\x02=\b\x13\x01\a\x03E6\x01\x03\b\x01\x02\x02\x02\x01\x02\x06\x02\x01\n\x01\x01\x05\x01\x02\x05\n\x01\x01\x01\x02\x01\f\x02\x02\x02\b\x01\x01\x01"},
{"net/http/cgi", "\x02P\x1b\x03{\x04\b\n\x01\x12\x01\x01\x01\x04\x01\x05\x02\n\x02\x01\x0f\x0e"},
{"net/http/cookiejar", "\x04i\x03\x91\x01\x01\b\v\x18\x03\x02\r\x04"},
{"net/http/fcgi", "\x02\x01\nY\a\x03{\x16\x01\x01\x13\x1a\x02\r"},
{"net/http/httptest", "\x02\x01\nE\x02\x1b\x01{\x04\x12\x01\t\t\x02\x19\x01\x02\r\x0e"},
{"net/http/httptrace", "\rEnA\x13\n!"},
{"net/http/httputil", "\x02\x01\n`\x03{\x04\x0f\x03\x01\x05\x02\x01\n\x01\x1b\x02\r\x0e"},
{"net/http/internal", "\x02\x01j\x03{"},
{"math/cmplx", "\xf7\x01\x02"},
{"math/rand", "\xb5\x01B;\x01\x12"},
{"math/rand/v2", "m,\x02\\\x02L"},
{"mime", "\x02\x01b\b\x03z\f \x17\x03\x02\x0f\x02"},
{"mime/multipart", "\x02\x01G#\x03E5\f\x01\x06\x02\x15\x02\x06\x11\x02\x01\x15"},
{"mime/quotedprintable", "\x02\x01mz"},
{"net", "\x04\t`+\x1d\a\x04\x05\f\x01\x04\x14\x01%\x06\r\n\x05\x01\x01\v\x06\a"},
{"net/http", "\x02\x01\x04\x04\x02=\b\x13\x01\a\x03E5\x01\x03\b\x01\x02\x02\x02\x01\x02\x06\x02\x01\x01\n\x01\x01\x05\x01\x02\x05\n\x01\x01\x01\x02\x01\x01\v\x02\x02\x02\b\x01\x01\x01"},
{"net/http/cgi", "\x02P\x1b\x03z\x04\b\n\x01\x13\x01\x01\x01\x04\x01\x05\x02\n\x02\x01\x0f\x0e"},
{"net/http/cookiejar", "\x04i\x03\x90\x01\x01\b\f\x18\x03\x02\r\x04"},
{"net/http/fcgi", "\x02\x01\nY\a\x03z\x16\x01\x01\x14\x1a\x02\r"},
{"net/http/httptest", "\x02\x01\nE\x02\x1b\x01z\x04\x12\x01\n\t\x02\x19\x01\x02\r\x0e"},
{"net/http/httptrace", "\rEn@\x14\n!"},
{"net/http/httputil", "\x02\x01\n`\x03z\x04\x0f\x03\x01\x05\x02\x01\v\x01\x1b\x02\r\x0e"},
{"net/http/internal", "\x02\x01j\x03z"},
{"net/http/internal/ascii", "\xb0\x02\x11"},
{"net/http/internal/httpcommon", "\r`\x03\x96\x01\x0e\x01\x19\x01\x01\x02\x1b\x02"},
{"net/http/internal/testcert", "\xb0\x02"},
{"net/http/pprof", "\x02\x01\nc\x19,\x11%\x04\x13\x13\x01\r\x06\x03\x01\x02\x01\x0f"},
{"net/http/pprof", "\x02\x01\nc\x19,\x11$\x04\x13\x14\x01\r\x06\x03\x01\x02\x01\x0f"},
{"net/internal/cgotest", ""},
{"net/internal/socktest", "p\xc2\x01\x02"},
{"net/mail", "\x02k\x03{\x04\x0f\x03\x13\x1c\x02\r\x04"},
{"net/netip", "\x04i+\x01#<\x025\x15"},
{"net/rpc", "\x02f\x05\x03\x10\na\x04\x12\x01\x1c\x0f\x03\x02"},
{"net/rpc/jsonrpc", "j\x03\x03{\x16\x10!"},
{"net/smtp", "\x19.\v\x13\b\x03{\x16\x13\x1c"},
{"net/textproto", "\x02\x01j\x03{\r\t.\x01\x02\x13"},
{"net/url", "m\x03\x87\x01$\x12\x02\x01\x15"},
{"os", "m+\x01\x18\x03\b\t\r\x03\x01\x04\x11\x017\n\x05\x01\x01\v\x06"},
{"os/exec", "\x03\n`H \x01\x15\x01*\x06\a\f\x01\x04\v"},
{"net/mail", "\x02k\x03z\x04\x0f\x03\x14\x1c\x02\r\x04"},
{"net/netip", "\x04i+\x01#;\x026\x15"},
{"net/rpc", "\x02f\x05\x03\x10\n`\x04\x12\x01\x1d\x0f\x03\x02"},
{"net/rpc/jsonrpc", "j\x03\x03z\x16\x11!"},
{"net/smtp", "\x19.\v\x13\b\x03z\x16\x14\x1c"},
{"net/textproto", "\x02\x01j\x03z\r\t/\x01\x02\x13"},
{"net/url", "m\x03\x86\x01%\x12\x02\x01\x15"},
{"os", "m+\x01\x18\x03\b\t\r\x03\x01\x04\x10\x018\n\x05\x01\x01\v\x06"},
{"os/exec", "\x03\n`H \x01\x14\x01+\x06\a\f\x01\x04\v"},
{"os/exec/internal/fdtest", "\xb4\x02"},
{"os/signal", "\r\x89\x02\x17\x05\x02"},
{"os/user", "\x02\x01j\x03{+\r\f\x01\x02"},
{"os/user", "\x02\x01j\x03z,\r\f\x01\x02"},
{"path", "m+\xab\x01"},
{"path/filepath", "m+\x19;*\r\n\x03\x04\x0f"},
{"path/filepath", "m+\x19:+\r\n\x03\x04\x0f"},
{"plugin", "m"},
{"reflect", "m'\x04\x1c\b\f\x04\x02\x1a\x06\n+\f\x03\x0f\x02\x02"},
{"reflect", "m'\x04\x1c\b\f\x04\x02\x19\x10,\f\x03\x0f\x02\x02"},
{"reflect/internal/example1", ""},
{"reflect/internal/example2", ""},
{"regexp", "\x03\xe8\x017\v\x02\x01\x02\x0f\x02"},
{"regexp", "\x03\xe7\x018\v\x02\x01\x02\x0f\x02"},
{"regexp/syntax", "\xad\x02\x01\x01\x01\x11\x02"},
{"runtime", "\x94\x01\x04\x01\x02\f\x06\a\x02\x01\x01\x0f\x03\x01\x01\x01\x01\x01\x03s"},
{"runtime/coverage", "\x9f\x01L"},
{"runtime", "\x94\x01\x04\x01\x02\f\x06\a\x02\x01\x01\x0f\x03\x01\x01\x01\x01\x01\x03\x0fd"},
{"runtime/coverage", "\x9f\x01K"},
{"runtime/debug", "pUQ\r\n\x02\x01\x0f\x06"},
{"runtime/internal/startlinetest", ""},
{"runtime/internal/wasitest", ""},
{"runtime/metrics", "\xb6\x01B+\""},
{"runtime/pprof", "\x02\x01\x01\x03\x06Y\a\x03$3$\r\x1e\r\n\x01\x01\x01\x02\x02\b\x03\x06"},
{"runtime/metrics", "\xb6\x01A,\""},
{"runtime/pprof", "\x02\x01\x01\x03\x06Y\a\x03$3#\r\x1f\r\n\x01\x01\x01\x02\x02\b\x03\x06"},
{"runtime/race", "\xab\x02"},
{"runtime/race/internal/amd64v1", ""},
{"runtime/trace", "\rc{8\x0f\x01\x12"},
{"slices", "\x04\xea\x01\fK"},
{"sort", "\xc9\x0113"},
{"strconv", "m+:&\x02I"},
{"strings", "m'\x04:\x19\x03\f8\x0f\x02\x02"},
{"runtime/trace", "\rcz9\x0f\x01\x12"},
{"slices", "\x04\xe9\x01\fL"},
{"sort", "\xc9\x0104"},
{"strconv", "m+:%\x02J"},
{"strings", "m'\x04:\x18\x03\f9\x0f\x02\x02"},
{"structs", ""},
{"sync", "\xc8\x01\vP\x10\x12"},
{"sync/atomic", "\xc5\x02"},
{"syscall", "m(\x03\x01\x1b\b\x03\x03\x06\aT\x0f\x01\x12"},
{"testing", "\x03\n`\x02\x01G\x11\x0f\x14\r\x04\x1a\x06\x02\x05\x02\a\x01\x02\x01\x02\x01\f\x02\x02\x02"},
{"testing/fstest", "m\x03{\x01\v$\x12\x03\b\a"},
{"testing/internal/testdeps", "\x02\v\xa6\x01'\x11+\x03\x05\x03\b\a\x02\r"},
{"testing/iotest", "\x03j\x03{\x04"},
{"testing/quick", "o\x01\x88\x01\x04\"\x12\x0f"},
{"testing/slogtest", "\r`\x03\x81\x01-\x05\x12\n"},
{"text/scanner", "\x03m{++\x02"},
{"text/tabwriter", "p{X"},
{"text/template", "m\x03B9\x01\v\x1e\x01\x05\x01\x02\x05\r\x02\f\x03\x02"},
{"syscall", "m(\x03\x01\x1b\b\x03\x03\x06\aT\n\x05\x01\x12"},
{"testing", "\x03\n`\x02\x01X\x0f\x13\r\x04\x1b\x06\x02\x05\x02\a\x01\x02\x01\x02\x01\f\x02\x02\x02"},
{"testing/fstest", "m\x03z\x01\v%\x12\x03\b\a"},
{"testing/internal/testdeps", "\x02\v\xa6\x01'\x10,\x03\x05\x03\b\a\x02\r"},
{"testing/iotest", "\x03j\x03z\x04"},
{"testing/quick", "o\x01\x87\x01\x04#\x12\x0f"},
{"testing/slogtest", "\r`\x03\x80\x01.\x05\x12\n"},
{"text/scanner", "\x03mz,+\x02"},
{"text/tabwriter", "pzY"},
{"text/template", "m\x03B8\x01\v\x1f\x01\x05\x01\x02\x05\r\x02\f\x03\x02"},
{"text/template/parse", "\x03m\xb3\x01\f\x01\x11\x02"},
{"time", "m+\x1d\x1d()\x0f\x02\x11"},
{"time", "m+\x1d\x1d'*\x0f\x02\x11"},
{"time/tzdata", "m\xc7\x01\x11"},
{"unicode", ""},
{"unicode/utf16", ""},
{"unicode/utf8", ""},
{"unique", "\x94\x01>\x01P\x0f\x13\x12"},
{"unsafe", ""},
{"vendor/golang.org/x/crypto/chacha20", "\x10V\a\x8d\x01)'"},
{"vendor/golang.org/x/crypto/chacha20", "\x10V\a\x8c\x01*'"},
{"vendor/golang.org/x/crypto/chacha20poly1305", "\x10V\a\xd9\x01\x04\x01\a"},
{"vendor/golang.org/x/crypto/cryptobyte", "c\n\x03\x89\x01%!\n"},
{"vendor/golang.org/x/crypto/cryptobyte", "c\n\x03\x88\x01&!\n"},
{"vendor/golang.org/x/crypto/cryptobyte/asn1", ""},
{"vendor/golang.org/x/crypto/internal/alias", "\xc5\x02"},
{"vendor/golang.org/x/crypto/internal/poly1305", "Q\x15\x94\x01"},
{"vendor/golang.org/x/crypto/internal/poly1305", "Q\x15\x93\x01"},
{"vendor/golang.org/x/net/dns/dnsmessage", "m"},
{"vendor/golang.org/x/net/http/httpguts", "\x81\x02\x13\x1c\x13\r"},
{"vendor/golang.org/x/net/http/httpproxy", "m\x03\x91\x01\x0f\x05\x01\x1a\x13\r"},
{"vendor/golang.org/x/net/http2/hpack", "\x03j\x03{G"},
{"vendor/golang.org/x/net/idna", "p\x88\x018\x13\x10\x02\x01"},
{"vendor/golang.org/x/net/nettest", "\x03c\a\x03{\x11\x05\x15\x01\f\f\x01\x02\x02\x01\n"},
{"vendor/golang.org/x/net/http/httpguts", "\x80\x02\x14\x1c\x13\r"},
{"vendor/golang.org/x/net/http/httpproxy", "m\x03\x90\x01\x15\x01\x1a\x13\r"},
{"vendor/golang.org/x/net/http2/hpack", "\x03j\x03zH"},
{"vendor/golang.org/x/net/idna", "p\x87\x019\x13\x10\x02\x01"},
{"vendor/golang.org/x/net/nettest", "\x03c\a\x03z\x11\x05\x16\x01\f\f\x01\x02\x02\x01\n"},
{"vendor/golang.org/x/sys/cpu", "\x96\x02\r\f\x01\x15"},
{"vendor/golang.org/x/text/secure/bidirule", "m\xd6\x01\x11\x01"},
{"vendor/golang.org/x/text/transform", "\x03j~X"},
{"vendor/golang.org/x/text/unicode/bidi", "\x03\be\x7f?\x15"},
{"vendor/golang.org/x/text/unicode/norm", "f\n{G\x11\x11"},
{"vendor/golang.org/x/text/transform", "\x03j}Y"},
{"vendor/golang.org/x/text/unicode/bidi", "\x03\be~@\x15"},
{"vendor/golang.org/x/text/unicode/norm", "f\nzH\x11\x11"},
{"weak", "\x94\x01\x8f\x01\""},
}

File diff suppressed because it is too large Load diff

View file

@ -18,6 +18,14 @@ type Symbol struct {
Name string
Kind Kind
Version Version // Go version that first included the symbol
// Signature provides the type of a function (defined only for Kind=Func).
// Imported types are denoted as pkg.T; pkg is not fully qualified.
// TODO(adonovan): use an unambiguous encoding that is parseable.
//
// Example2:
// func[M ~map[K]V, K comparable, V any](m M) M
// func(fi fs.FileInfo, link string) (*Header, error)
Signature string // if Kind == stdlib.Func
}
// A Kind indicates the kind of a symbol:

View file

@ -65,14 +65,16 @@ func ClassifyCall(info *types.Info, call *ast.CallExpr) CallKind {
if info.Types == nil {
panic("ClassifyCall: info.Types is nil")
}
if info.Types[call.Fun].IsType() {
tv := info.Types[call.Fun]
if tv.IsType() {
return CallConversion
}
if tv.IsBuiltin() {
return CallBuiltin
}
obj := info.Uses[UsedIdent(info, call.Fun)]
// Classify the call by the type of the object, if any.
switch obj := obj.(type) {
case *types.Builtin:
return CallBuiltin
case *types.Func:
if interfaceMethod(obj) {
return CallInterface

View file

@ -4,7 +4,7 @@
// Package typeindex provides an [Index] of type information for a
// package, allowing efficient lookup of, say, whether a given symbol
// is referenced and, if so, where from; or of the [cursor.Cursor] for
// is referenced and, if so, where from; or of the [inspector.Cursor] for
// the declaration of a particular [types.Object] symbol.
package typeindex
@ -14,10 +14,9 @@ import (
"go/types"
"iter"
"golang.org/x/tools/go/ast/edge"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/astutil/cursor"
"golang.org/x/tools/internal/astutil/edge"
"golang.org/x/tools/internal/typesinternal"
)
@ -30,7 +29,7 @@ func New(inspect *inspector.Inspector, pkg *types.Package, info *types.Info) *In
inspect: inspect,
info: info,
packages: make(map[string]*types.Package),
def: make(map[types.Object]cursor.Cursor),
def: make(map[types.Object]inspector.Cursor),
uses: make(map[types.Object]*uses),
}
@ -40,7 +39,7 @@ func New(inspect *inspector.Inspector, pkg *types.Package, info *types.Info) *In
}
}
for cur := range cursor.Root(inspect).Preorder((*ast.ImportSpec)(nil), (*ast.Ident)(nil)) {
for cur := range inspect.Root().Preorder((*ast.ImportSpec)(nil), (*ast.Ident)(nil)) {
switch n := cur.Node().(type) {
case *ast.ImportSpec:
// Index direct imports, including blank ones.
@ -84,7 +83,7 @@ type Index struct {
inspect *inspector.Inspector
info *types.Info
packages map[string]*types.Package // packages of all symbols referenced from this package
def map[types.Object]cursor.Cursor // Cursor of *ast.Ident that defines the Object
def map[types.Object]inspector.Cursor // Cursor of *ast.Ident that defines the Object
uses map[types.Object]*uses // Cursors of *ast.Idents that use the Object
}
@ -107,14 +106,14 @@ type uses struct {
// Uses returns the sequence of Cursors of [*ast.Ident]s in this package
// that refer to obj. If obj is nil, the sequence is empty.
func (ix *Index) Uses(obj types.Object) iter.Seq[cursor.Cursor] {
return func(yield func(cursor.Cursor) bool) {
func (ix *Index) Uses(obj types.Object) iter.Seq[inspector.Cursor] {
return func(yield func(inspector.Cursor) bool) {
if uses := ix.uses[obj]; uses != nil {
var last int32
for code := uses.code; len(code) > 0; {
delta, n := binary.Uvarint(code)
last += int32(delta)
if !yield(cursor.At(ix.inspect, last)) {
if !yield(ix.inspect.At(last)) {
return
}
code = code[n:]
@ -140,7 +139,7 @@ func (ix *Index) Used(objs ...types.Object) bool {
// Def returns the Cursor of the [*ast.Ident] in this package
// that declares the specified object, if any.
func (ix *Index) Def(obj types.Object) (cursor.Cursor, bool) {
func (ix *Index) Def(obj types.Object) (inspector.Cursor, bool) {
cur, ok := ix.def[obj]
return cur, ok
}
@ -176,8 +175,8 @@ func (ix *Index) Selection(path, typename, name string) types.Object {
// Calls returns the sequence of cursors for *ast.CallExpr nodes that
// call the specified callee, as defined by [typeutil.Callee].
// If callee is nil, the sequence is empty.
func (ix *Index) Calls(callee types.Object) iter.Seq[cursor.Cursor] {
return func(yield func(cursor.Cursor) bool) {
func (ix *Index) Calls(callee types.Object) iter.Seq[inspector.Cursor] {
return func(yield func(inspector.Cursor) bool) {
for cur := range ix.Uses(callee) {
ek, _ := cur.ParentEdge()

View file

@ -69,6 +69,34 @@ func NameRelativeTo(pkg *types.Package) types.Qualifier {
}
}
// TypeNameFor returns the type name symbol for the specified type, if
// it is a [*types.Alias], [*types.Named], [*types.TypeParam], or a
// [*types.Basic] representing a type.
//
// For all other types, and for Basic types representing a builtin,
// constant, or nil, it returns nil. Be careful not to convert the
// resulting nil pointer to a [types.Object]!
//
// If t is the type of a constant, it may be an "untyped" type, which
// has no TypeName. To access the name of such types (e.g. "untyped
// int"), use [types.Basic.Name].
func TypeNameFor(t types.Type) *types.TypeName {
switch t := t.(type) {
case *types.Alias:
return t.Obj()
case *types.Named:
return t.Obj()
case *types.TypeParam:
return t.Obj()
case *types.Basic:
// See issues #71886 and #66890 for some history.
if tname, ok := types.Universe.Lookup(t.Name()).(*types.TypeName); ok {
return tname
}
}
return nil
}
// A NamedOrAlias is a [types.Type] that is named (as
// defined by the spec) and capable of bearing type parameters: it
// abstracts aliases ([types.Alias]) and defined types
@ -77,7 +105,7 @@ func NameRelativeTo(pkg *types.Package) types.Qualifier {
// Every type declared by an explicit "type" declaration is a
// NamedOrAlias. (Built-in type symbols may additionally
// have type [types.Basic], which is not a NamedOrAlias,
// though the spec regards them as "named".)
// though the spec regards them as "named"; see [TypeNameFor].)
//
// NamedOrAlias cannot expose the Origin method, because
// [types.Alias.Origin] and [types.Named.Origin] have different
@ -85,32 +113,15 @@ func NameRelativeTo(pkg *types.Package) types.Qualifier {
type NamedOrAlias interface {
types.Type
Obj() *types.TypeName
// TODO(hxjiang): add method TypeArgs() *types.TypeList after stop supporting go1.22.
TypeArgs() *types.TypeList
TypeParams() *types.TypeParamList
SetTypeParams(tparams []*types.TypeParam)
}
// TypeParams is a light shim around t.TypeParams().
// (go/types.Alias).TypeParams requires >= 1.23.
func TypeParams(t NamedOrAlias) *types.TypeParamList {
switch t := t.(type) {
case *types.Alias:
return aliases.TypeParams(t)
case *types.Named:
return t.TypeParams()
}
return nil
}
// TypeArgs is a light shim around t.TypeArgs().
// (go/types.Alias).TypeArgs requires >= 1.23.
func TypeArgs(t NamedOrAlias) *types.TypeList {
switch t := t.(type) {
case *types.Alias:
return aliases.TypeArgs(t)
case *types.Named:
return t.TypeArgs()
}
return nil
}
var (
_ NamedOrAlias = (*types.Alias)(nil)
_ NamedOrAlias = (*types.Named)(nil)
)
// Origin returns the generic type of the Named or Alias type t if it
// is instantiated, otherwise it returns t.

View file

@ -16,8 +16,8 @@ github.com/google/pprof/third_party/svgpan
# github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd
## explicit; go 1.13
github.com/ianlancetaylor/demangle
# golang.org/x/arch v0.14.0
## explicit; go 1.18
# golang.org/x/arch v0.18.1-0.20250605182141-b2f4e2807dec
## explicit; go 1.23.0
golang.org/x/arch/arm/armasm
golang.org/x/arch/arm64/arm64asm
golang.org/x/arch/loong64/loong64asm
@ -25,10 +25,10 @@ golang.org/x/arch/ppc64/ppc64asm
golang.org/x/arch/riscv64/riscv64asm
golang.org/x/arch/s390x/s390xasm
golang.org/x/arch/x86/x86asm
# golang.org/x/build v0.0.0-20250211223606-a5e3f75caa63
## explicit; go 1.22.0
# golang.org/x/build v0.0.0-20250606033421-8c8ff6f34a83
## explicit; go 1.23.0
golang.org/x/build/relnote
# golang.org/x/mod v0.24.1-0.20250508140430-9d3333156f46
# golang.org/x/mod v0.25.0
## explicit; go 1.23.0
golang.org/x/mod/internal/lazyregexp
golang.org/x/mod/modfile
@ -39,17 +39,17 @@ golang.org/x/mod/sumdb/dirhash
golang.org/x/mod/sumdb/note
golang.org/x/mod/sumdb/tlog
golang.org/x/mod/zip
# golang.org/x/sync v0.13.0
# golang.org/x/sync v0.15.0
## explicit; go 1.23.0
golang.org/x/sync/errgroup
golang.org/x/sync/semaphore
# golang.org/x/sys v0.32.0
# golang.org/x/sys v0.33.0
## explicit; go 1.23.0
golang.org/x/sys/plan9
golang.org/x/sys/unix
golang.org/x/sys/windows
# golang.org/x/telemetry v0.0.0-20250212145848-75305293b65a
## explicit; go 1.22.0
# golang.org/x/telemetry v0.0.0-20250606142133-60998feb31a8
## explicit; go 1.23.0
golang.org/x/telemetry
golang.org/x/telemetry/counter
golang.org/x/telemetry/counter/countertest
@ -60,10 +60,10 @@ golang.org/x/telemetry/internal/crashmonitor
golang.org/x/telemetry/internal/mmap
golang.org/x/telemetry/internal/telemetry
golang.org/x/telemetry/internal/upload
# golang.org/x/term v0.29.0
## explicit; go 1.18
# golang.org/x/term v0.32.0
## explicit; go 1.23.0
golang.org/x/term
# golang.org/x/text v0.24.0
# golang.org/x/text v0.26.0
## explicit; go 1.23.0
golang.org/x/text/cases
golang.org/x/text/internal
@ -73,7 +73,7 @@ golang.org/x/text/internal/tag
golang.org/x/text/language
golang.org/x/text/transform
golang.org/x/text/unicode/norm
# golang.org/x/tools v0.32.1-0.20250423190156-68e94bd1775e
# golang.org/x/tools v0.34.0
## explicit; go 1.23.0
golang.org/x/tools/cmd/bisect
golang.org/x/tools/cover
@ -118,6 +118,7 @@ golang.org/x/tools/go/analysis/passes/unsafeptr
golang.org/x/tools/go/analysis/passes/unusedresult
golang.org/x/tools/go/analysis/passes/waitgroup
golang.org/x/tools/go/analysis/unitchecker
golang.org/x/tools/go/ast/edge
golang.org/x/tools/go/ast/inspector
golang.org/x/tools/go/cfg
golang.org/x/tools/go/types/objectpath
@ -126,8 +127,6 @@ golang.org/x/tools/internal/aliases
golang.org/x/tools/internal/analysisinternal
golang.org/x/tools/internal/analysisinternal/typeindex
golang.org/x/tools/internal/astutil
golang.org/x/tools/internal/astutil/cursor
golang.org/x/tools/internal/astutil/edge
golang.org/x/tools/internal/bisect
golang.org/x/tools/internal/facts
golang.org/x/tools/internal/fmtstr

View file

@ -3,11 +3,11 @@ module std
go 1.25
require (
golang.org/x/crypto v0.37.0
golang.org/x/net v0.39.0
golang.org/x/crypto v0.39.0
golang.org/x/net v0.41.0
)
require (
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
)

View file

@ -1,8 +1,8 @@
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=

View file

@ -4,6 +4,10 @@
// Package ast declares the types used to represent syntax trees for Go
// packages.
//
// Syntax trees may be constructed directly, but they are typically
// produced from Go source code by the parser; see the ParseFile
// function in package [go/parser].
package ast
import (

View file

@ -2,10 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package parser implements a parser for Go source files. Input may be
// provided in a variety of forms (see the various Parse* functions); the
// output is an abstract syntax tree (AST) representing the Go source. The
// parser is invoked through one of the Parse* functions.
// Package parser implements a parser for Go source files.
//
// The [ParseFile] function reads file input from a string, []byte, or
// io.Reader, and produces an [ast.File] representing the complete
// abstract syntax tree of the file.
//
// The [ParseExprFrom] function reads a single source-level expression and
// produces an [ast.Expr], the syntax tree of the expression.
//
// The parser accepts a larger language than is syntactically permitted by
// the Go spec, for simplicity, and for improved robustness in the presence
@ -13,6 +17,11 @@
// treated like an ordinary parameter list and thus may contain multiple
// entries where the spec permits exactly one. Consequently, the corresponding
// field in the AST (ast.FuncDecl.Recv) field is not restricted to one entry.
//
// Applications that need to parse one or more complete packages of Go
// source code may find it more convenient not to interact directly
// with the parser but instead to use the Load function in package
// [golang.org/x/tools/go/packages].
package parser
import (

View file

@ -429,7 +429,7 @@ func (f *File) Position(p Pos) (pos Position) {
type FileSet struct {
mutex sync.RWMutex // protects the file set
base int // base offset for the next file
files []*File // list of files in the order added to the set
tree tree // tree of files in ascending base order
last atomic.Pointer[File] // cache of last file looked up
}
@ -487,7 +487,7 @@ func (s *FileSet) AddFile(filename string, base, size int) *File {
}
// add the file to the file set
s.base = base
s.files = append(s.files, f)
s.tree.add(f)
s.last.Store(f)
return f
}
@ -518,40 +518,9 @@ func (s *FileSet) AddExistingFiles(files ...*File) {
s.mutex.Lock()
defer s.mutex.Unlock()
// Merge and sort.
newFiles := append(s.files, files...)
slices.SortFunc(newFiles, func(x, y *File) int {
return cmp.Compare(x.Base(), y.Base())
})
// Reject overlapping files.
// Discard adjacent identical files.
out := newFiles[:0]
for i, file := range newFiles {
if i > 0 {
prev := newFiles[i-1]
if file == prev {
continue
}
if prev.Base()+prev.Size()+1 > file.Base() {
panic(fmt.Sprintf("file %s (%d-%d) overlaps with file %s (%d-%d)",
prev.Name(), prev.Base(), prev.Base()+prev.Size(),
file.Name(), file.Base(), file.Base()+file.Size()))
}
}
out = append(out, file)
}
newFiles = out
s.files = newFiles
// Advance base.
if len(newFiles) > 0 {
last := newFiles[len(newFiles)-1]
newBase := last.Base() + last.Size() + 1
if s.base < newBase {
s.base = newBase
}
for _, f := range files {
s.tree.add(f)
s.base = max(s.base, f.Base()+f.Size()+1)
}
}
@ -567,39 +536,26 @@ func (s *FileSet) RemoveFile(file *File) {
s.mutex.Lock()
defer s.mutex.Unlock()
if i := searchFiles(s.files, file.base); i >= 0 && s.files[i] == file {
last := &s.files[len(s.files)-1]
s.files = slices.Delete(s.files, i, i+1)
*last = nil // don't prolong lifetime when popping last element
pn, _ := s.tree.locate(file.key())
if *pn != nil && (*pn).file == file {
s.tree.delete(pn)
}
}
// Iterate calls f for the files in the file set in the order they were added
// until f returns false.
func (s *FileSet) Iterate(f func(*File) bool) {
for i := 0; ; i++ {
var file *File
// Iterate calls yield for the files in the file set in ascending Base
// order until yield returns false.
func (s *FileSet) Iterate(yield func(*File) bool) {
s.mutex.RLock()
if i < len(s.files) {
file = s.files[i]
}
s.mutex.RUnlock()
if file == nil || !f(file) {
break
}
}
}
defer s.mutex.RUnlock()
func searchFiles(a []*File, x int) int {
i, found := slices.BinarySearchFunc(a, x, func(a *File, x int) int {
return cmp.Compare(a.base, x)
// Unlock around user code.
// The iterator is robust to modification by yield.
// Avoid range here, so we can use defer.
s.tree.all()(func(f *File) bool {
s.mutex.RUnlock()
defer s.mutex.RLock()
return yield(f)
})
if !found {
// We want the File containing x, but if we didn't
// find x then i is the next one.
i--
}
return i
}
func (s *FileSet) file(p Pos) *File {
@ -611,16 +567,12 @@ func (s *FileSet) file(p Pos) *File {
s.mutex.RLock()
defer s.mutex.RUnlock()
// p is not in last file - search all files
if i := searchFiles(s.files, int(p)); i >= 0 {
f := s.files[i]
// f.base <= int(p) by definition of searchFiles
if int(p) <= f.base+f.size {
pn, _ := s.tree.locate(key{int(p), int(p)})
if n := *pn; n != nil {
// Update cache of last file. A race is ok,
// but an exclusive lock causes heavy contention.
s.last.Store(f)
return f
}
s.last.Store(n.file)
return n.file
}
return nil
}

View file

@ -84,15 +84,15 @@ func BenchmarkFileSet_Position(b *testing.B) {
}
func BenchmarkFileSet_AddExistingFiles(b *testing.B) {
rng := rand.New(rand.NewPCG(rand.Uint64(), rand.Uint64()))
// Create the "universe" of files.
fset := token.NewFileSet()
var files []*token.File
for range 25000 {
files = append(files, fset.AddFile("", -1, 10000))
}
rand.Shuffle(len(files), func(i, j int) {
files[i], files[j] = files[j], files[i]
})
token.Shuffle(rng, files)
// choose returns n random files.
choose := func(n int) []*token.File {

View file

@ -4,6 +4,8 @@
package token
import "slices"
type serializedFile struct {
// fields correspond 1:1 to fields with same (lower-case) name in File
Name string
@ -27,18 +29,15 @@ func (s *FileSet) Read(decode func(any) error) error {
s.mutex.Lock()
s.base = ss.Base
files := make([]*File, len(ss.Files))
for i := 0; i < len(ss.Files); i++ {
f := &ss.Files[i]
files[i] = &File{
for _, f := range ss.Files {
s.tree.add(&File{
name: f.Name,
base: f.Base,
size: f.Size,
lines: f.Lines,
infos: f.Infos,
})
}
}
s.files = files
s.last.Store(nil)
s.mutex.Unlock()
@ -51,16 +50,16 @@ func (s *FileSet) Write(encode func(any) error) error {
s.mutex.Lock()
ss.Base = s.base
files := make([]serializedFile, len(s.files))
for i, f := range s.files {
var files []serializedFile
for f := range s.tree.all() {
f.mutex.Lock()
files[i] = serializedFile{
files = append(files, serializedFile{
Name: f.name,
Base: f.base,
Size: f.size,
Lines: append([]int(nil), f.lines...),
Infos: append([]lineInfo(nil), f.infos...),
}
Lines: slices.Clone(f.lines),
Infos: slices.Clone(f.infos),
})
f.mutex.Unlock()
}
ss.Files = files

View file

@ -8,6 +8,7 @@ import (
"bytes"
"encoding/gob"
"fmt"
"slices"
"testing"
)
@ -29,12 +30,14 @@ func equal(p, q *FileSet) error {
return fmt.Errorf("different bases: %d != %d", p.base, q.base)
}
if len(p.files) != len(q.files) {
return fmt.Errorf("different number of files: %d != %d", len(p.files), len(q.files))
pfiles := slices.Collect(p.tree.all())
qfiles := slices.Collect(q.tree.all())
if len(pfiles) != len(qfiles) {
return fmt.Errorf("different number of files: %d != %d", len(pfiles), len(qfiles))
}
for i, f := range p.files {
g := q.files[i]
for i, f := range pfiles {
g := qfiles[i]
if f.name != g.name {
return fmt.Errorf("different filenames: %q != %q", f.name, g.name)
}
@ -88,7 +91,7 @@ func TestSerialization(t *testing.T) {
p := NewFileSet()
checkSerialize(t, p)
// add some files
for i := 0; i < 10; i++ {
for i := range 10 {
f := p.AddFile(fmt.Sprintf("file%d", i), p.Base()+i, i*100)
checkSerialize(t, p)
// add some lines and alternative file infos

410
src/go/token/tree.go Normal file
View file

@ -0,0 +1,410 @@
// Copyright 2025 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 token
// tree is a self-balancing AVL tree; see
// Lewis & Denenberg, Data Structures and Their Algorithms.
//
// An AVL tree is a binary tree in which the difference between the
// heights of a node's two subtrees--the node's "balance factor"--is
// at most one. It is more strictly balanced than a red/black tree,
// and thus favors lookups at the expense of updates, which is the
// appropriate trade-off for FileSet.
//
// Insertion at a node may cause its ancestors' balance factors to
// temporarily reach ±2, requiring rebalancing of each such ancestor
// by a rotation.
//
// Each key is the pos-end range of a single File.
// All Files in the tree must have disjoint ranges.
//
// The implementation is simplified from Russ Cox's github.com/rsc/omap.
import (
"fmt"
"iter"
)
// A tree is a tree-based ordered map:
// each value is a *File, keyed by its Pos range.
// All map entries cover disjoint ranges.
//
// The zero value of tree is an empty map ready to use.
type tree struct {
root *node
}
type node struct {
// We use the notation (parent left right) in many comments.
parent *node
left *node
right *node
file *File
key key // = file.key(), but improves locality (25% faster)
balance int32 // at most ±2
height int32
}
// A key represents the Pos range of a File.
type key struct{ start, end int }
func (f *File) key() key {
return key{f.base, f.base + f.size}
}
// compareKey reports whether x is before y (-1),
// after y (+1), or overlapping y (0).
// This is a total order so long as all
// files in the tree have disjoint ranges.
//
// All files are separated by at least one unit.
// This allows us to use strict < comparisons.
// Use key{p, p} to search for a zero-width position
// even at the start or end of a file.
func compareKey(x, y key) int {
switch {
case x.end < y.start:
return -1
case y.end < x.start:
return +1
}
return 0
}
// check asserts that each node's height, subtree, and parent link is
// correct.
func (n *node) check(parent *node) {
const debugging = false
if debugging {
if n == nil {
return
}
if n.parent != parent {
panic("bad parent")
}
n.left.check(n)
n.right.check(n)
n.checkBalance()
}
}
func (n *node) checkBalance() {
lheight, rheight := n.left.safeHeight(), n.right.safeHeight()
balance := rheight - lheight
if balance != n.balance {
panic("bad node.balance")
}
if !(-2 <= balance && balance <= +2) {
panic(fmt.Sprintf("node.balance out of range: %d", balance))
}
h := 1 + max(lheight, rheight)
if h != n.height {
panic("bad node.height")
}
}
// locate returns a pointer to the variable that holds the node
// identified by k, along with its parent, if any. If the key is not
// present, it returns a pointer to the node where the key should be
// inserted by a subsequent call to [tree.set].
func (t *tree) locate(k key) (pos **node, parent *node) {
pos, x := &t.root, t.root
for x != nil {
sign := compareKey(k, x.key)
if sign < 0 {
pos, x, parent = &x.left, x.left, x
} else if sign > 0 {
pos, x, parent = &x.right, x.right, x
} else {
break
}
}
return pos, parent
}
// all returns an iterator over the tree t.
// If t is modified during the iteration,
// some files may not be visited.
// No file will be visited multiple times.
func (t *tree) all() iter.Seq[*File] {
return func(yield func(*File) bool) {
if t == nil {
return
}
x := t.root
if x != nil {
for x.left != nil {
x = x.left
}
}
for x != nil && yield(x.file) {
if x.height >= 0 {
// still in tree
x = x.next()
} else {
// deleted
x = t.nextAfter(t.locate(x.key))
}
}
}
}
// nextAfter returns the node in the key sequence following
// (pos, parent), a result pair from [tree.locate].
func (t *tree) nextAfter(pos **node, parent *node) *node {
switch {
case *pos != nil:
return (*pos).next()
case parent == nil:
return nil
case pos == &parent.left:
return parent
default:
return parent.next()
}
}
func (x *node) next() *node {
if x.right == nil {
for x.parent != nil && x.parent.right == x {
x = x.parent
}
return x.parent
}
x = x.right
for x.left != nil {
x = x.left
}
return x
}
func (t *tree) setRoot(x *node) {
t.root = x
if x != nil {
x.parent = nil
}
}
func (x *node) setLeft(y *node) {
x.left = y
if y != nil {
y.parent = x
}
}
func (x *node) setRight(y *node) {
x.right = y
if y != nil {
y.parent = x
}
}
func (n *node) safeHeight() int32 {
if n == nil {
return -1
}
return n.height
}
func (n *node) update() {
lheight, rheight := n.left.safeHeight(), n.right.safeHeight()
n.height = max(lheight, rheight) + 1
n.balance = rheight - lheight
}
func (t *tree) replaceChild(parent, old, new *node) {
switch {
case parent == nil:
if t.root != old {
panic("corrupt tree")
}
t.setRoot(new)
case parent.left == old:
parent.setLeft(new)
case parent.right == old:
parent.setRight(new)
default:
panic("corrupt tree")
}
}
// rebalanceUp visits each excessively unbalanced ancestor
// of x, restoring balance by rotating it.
//
// x is a node that has just been mutated, and so the height and
// balance of x and its ancestors may be stale, but the children of x
// must be in a valid state.
func (t *tree) rebalanceUp(x *node) {
for x != nil {
h := x.height
x.update()
switch x.balance {
case -2:
if x.left.balance == 1 {
t.rotateLeft(x.left)
}
x = t.rotateRight(x)
case +2:
if x.right.balance == -1 {
t.rotateRight(x.right)
}
x = t.rotateLeft(x)
}
if x.height == h {
// x's height has not changed, so the height
// and balance of its ancestors have not changed;
// no further rebalancing is required.
return
}
x = x.parent
}
}
// rotateRight rotates the subtree rooted at node y.
// turning (y (x a b) c) into (x a (y b c)).
func (t *tree) rotateRight(y *node) *node {
// p -> (y (x a b) c)
p := y.parent
x := y.left
b := x.right
x.checkBalance()
y.checkBalance()
x.setRight(y)
y.setLeft(b)
t.replaceChild(p, y, x)
y.update()
x.update()
return x
}
// rotateLeft rotates the subtree rooted at node x.
// turning (x a (y b c)) into (y (x a b) c).
func (t *tree) rotateLeft(x *node) *node {
// p -> (x a (y b c))
p := x.parent
y := x.right
b := y.left
x.checkBalance()
y.checkBalance()
y.setLeft(x)
x.setRight(b)
t.replaceChild(p, x, y)
x.update()
y.update()
return y
}
// add inserts file into the tree, if not present.
// It panics if file overlaps with another.
func (t *tree) add(file *File) {
pos, parent := t.locate(file.key())
if *pos == nil {
t.set(file, pos, parent) // missing; insert
return
}
if prev := (*pos).file; prev != file {
panic(fmt.Sprintf("file %s (%d-%d) overlaps with file %s (%d-%d)",
prev.Name(), prev.Base(), prev.Base()+prev.Size(),
file.Name(), file.Base(), file.Base()+file.Size()))
}
}
// set updates the existing node at (pos, parent) if present, or
// inserts a new node if not, so that it refers to file.
func (t *tree) set(file *File, pos **node, parent *node) {
if x := *pos; x != nil {
// This code path isn't currently needed
// because FileSet never updates an existing entry.
// Remove this assertion if things change.
if true {
panic("unreachable according to current FileSet requirements")
}
x.file = file
return
}
x := &node{file: file, key: file.key(), parent: parent, height: -1}
*pos = x
t.rebalanceUp(x)
}
// delete deletes the node at pos.
func (t *tree) delete(pos **node) {
t.root.check(nil)
x := *pos
switch {
case x == nil:
// This code path isn't currently needed because FileSet
// only calls delete after a positive locate.
// Remove this assertion if things change.
if true {
panic("unreachable according to current FileSet requirements")
}
return
case x.left == nil:
if *pos = x.right; *pos != nil {
(*pos).parent = x.parent
}
t.rebalanceUp(x.parent)
case x.right == nil:
*pos = x.left
x.left.parent = x.parent
t.rebalanceUp(x.parent)
default:
t.deleteSwap(pos)
}
x.balance = -100
x.parent = nil
x.left = nil
x.right = nil
x.height = -1
t.root.check(nil)
}
// deleteSwap deletes a node that has two children by replacing
// it by its in-order successor, then triggers a rebalance.
func (t *tree) deleteSwap(pos **node) {
x := *pos
z := t.deleteMin(&x.right)
*pos = z
unbalanced := z.parent // lowest potentially unbalanced node
if unbalanced == x {
unbalanced = z // (x a (z nil b)) -> (z a b)
}
z.parent = x.parent
z.height = x.height
z.balance = x.balance
z.setLeft(x.left)
z.setRight(x.right)
t.rebalanceUp(unbalanced)
}
// deleteMin updates the subtree rooted at *zpos to delete its minimum
// (leftmost) element, which may be *zpos itself. It returns the
// deleted node.
func (t *tree) deleteMin(zpos **node) (z *node) {
for (*zpos).left != nil {
zpos = &(*zpos).left
}
z = *zpos
*zpos = z.right
if *zpos != nil {
(*zpos).parent = z.parent
}
return z
}

86
src/go/token/tree_test.go Normal file
View file

@ -0,0 +1,86 @@
// Copyright 2025 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 token
import (
"math/rand/v2"
"slices"
"testing"
)
// TestTree provides basic coverage of the AVL tree operations.
func TestTree(t *testing.T) {
// Use a reproducible PRNG.
seed1, seed2 := rand.Uint64(), rand.Uint64()
t.Logf("random seeds: %d, %d", seed1, seed2)
rng := rand.New(rand.NewPCG(seed1, seed2))
// Create a number of Files of arbitrary size.
files := make([]*File, 500)
var base int
for i := range files {
base++
size := 1000
files[i] = &File{base: base, size: size}
base += size
}
// Add them all to the tree in random order.
var tr tree
{
files2 := slices.Clone(files)
Shuffle(rng, files2)
for _, f := range files2 {
tr.add(f)
}
}
// Randomly delete a subset of them.
for range 100 {
i := rng.IntN(len(files))
file := files[i]
if file == nil {
continue // already deleted
}
files[i] = nil
pn, _ := tr.locate(file.key())
if (*pn).file != file {
t.Fatalf("locate returned wrong file")
}
tr.delete(pn)
}
// Check some position lookups within each file.
for _, file := range files {
if file == nil {
continue // deleted
}
for _, pos := range []int{
file.base, // start
file.base + file.size/2, // midpoint
file.base + file.size, // end
} {
pn, _ := tr.locate(key{pos, pos})
if (*pn).file != file {
t.Fatalf("lookup %s@%d returned wrong file %s",
file.name, pos,
(*pn).file.name)
}
}
}
// Check that the sequence is the same.
files = slices.DeleteFunc(files, func(f *File) bool { return f == nil })
if !slices.Equal(slices.Collect(tr.all()), files) {
t.Fatalf("incorrect tree.all sequence")
}
}
func Shuffle[T any](rng *rand.Rand, slice []*T) {
rng.Shuffle(len(slice), func(i, j int) {
slice[i], slice[j] = slice[j], slice[i]
})
}

View file

@ -26,6 +26,11 @@
// specification. Use the Types field of [Info] for the results of
// type deduction.
//
// Applications that need to type-check one or more complete packages
// of Go source code may find it more convenient not to invoke the
// type checker directly but instead to use the Load function in
// package [golang.org/x/tools/go/packages].
//
// For a tutorial, see https://go.dev/s/types-tutorial.
package types

View file

@ -335,6 +335,7 @@ func TestStdFixed(t *testing.T) {
"issue56103.go", // anonymous interface cycles; will be a type checker error in 1.22
"issue52697.go", // go/types does not have constraints on stack size
"issue73309.go", // this test requires GODEBUG=gotypesalias=1
"issue73309b.go", // this test requires GODEBUG=gotypesalias=1
// These tests requires runtime/cgo.Incomplete, which is only available on some platforms.
// However, go/types does not know about build constraints.

View file

@ -16,6 +16,7 @@ import (
"strconv"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
"weak"
@ -218,6 +219,116 @@ func TestTimerFromOutsideBubble(t *testing.T) {
}
}
// TestTimerNondeterminism verifies that timers firing at the same instant
// don't always fire in exactly the same order.
func TestTimerNondeterminism(t *testing.T) {
synctest.Run(func() {
const iterations = 1000
var seen1, seen2 bool
for range iterations {
tm1 := time.NewTimer(1)
tm2 := time.NewTimer(1)
select {
case <-tm1.C:
seen1 = true
case <-tm2.C:
seen2 = true
}
if seen1 && seen2 {
return
}
synctest.Wait()
}
t.Errorf("after %v iterations, seen timer1:%v, timer2:%v; want both", iterations, seen1, seen2)
})
}
// TestSleepNondeterminism verifies that goroutines sleeping to the same instant
// don't always schedule in exactly the same order.
func TestSleepNondeterminism(t *testing.T) {
synctest.Run(func() {
const iterations = 1000
var seen1, seen2 bool
for range iterations {
var first atomic.Int32
go func() {
time.Sleep(1)
first.CompareAndSwap(0, 1)
}()
go func() {
time.Sleep(1)
first.CompareAndSwap(0, 2)
}()
time.Sleep(1)
synctest.Wait()
switch v := first.Load(); v {
case 1:
seen1 = true
case 2:
seen2 = true
default:
t.Fatalf("first = %v, want 1 or 2", v)
}
if seen1 && seen2 {
return
}
synctest.Wait()
}
t.Errorf("after %v iterations, seen goroutine 1:%v, 2:%v; want both", iterations, seen1, seen2)
})
}
// TestTimerRunsImmediately verifies that a 0-duration timer sends on its channel
// without waiting for the bubble to block.
func TestTimerRunsImmediately(t *testing.T) {
synctest.Run(func() {
start := time.Now()
tm := time.NewTimer(0)
select {
case got := <-tm.C:
if !got.Equal(start) {
t.Errorf("<-tm.C = %v, want %v", got, start)
}
default:
t.Errorf("0-duration timer channel is not readable; want it to be")
}
})
}
// TestTimerRunsLater verifies that reading from a timer's channel receives the
// timer fired, even when that time is in reading from a timer's channel receives the
// time the timer fired, even when that time is in the past.
func TestTimerRanInPast(t *testing.T) {
synctest.Run(func() {
delay := 1 * time.Second
want := time.Now().Add(delay)
tm := time.NewTimer(delay)
time.Sleep(2 * delay)
select {
case got := <-tm.C:
if !got.Equal(want) {
t.Errorf("<-tm.C = %v, want %v", got, want)
}
default:
t.Errorf("0-duration timer channel is not readable; want it to be")
}
})
}
// TestAfterFuncRunsImmediately verifies that a 0-duration AfterFunc is scheduled
// without waiting for the bubble to block.
func TestAfterFuncRunsImmediately(t *testing.T) {
synctest.Run(func() {
var b atomic.Bool
time.AfterFunc(0, func() {
b.Store(true)
})
for !b.Load() {
runtime.Gosched()
}
})
}
func TestChannelFromOutsideBubble(t *testing.T) {
choutside := make(chan struct{})
for _, test := range []struct {
@ -377,7 +488,7 @@ func TestDeadlockRoot(t *testing.T) {
}
func TestDeadlockChild(t *testing.T) {
defer wantPanic(t, "deadlock: all goroutines in bubble are blocked")
defer wantPanic(t, "deadlock: main bubble goroutine has exited but blocked goroutines remain")
synctest.Run(func() {
go func() {
select {}
@ -386,7 +497,7 @@ func TestDeadlockChild(t *testing.T) {
}
func TestDeadlockTicker(t *testing.T) {
defer wantPanic(t, "deadlock: all goroutines in bubble are blocked")
defer wantPanic(t, "deadlock: main bubble goroutine has exited but blocked goroutines remain")
synctest.Run(func() {
go func() {
for range time.Tick(1 * time.Second) {

View file

@ -600,6 +600,10 @@ func testTraceProg(t *testing.T, progName string, extra func(t *testing.T, trace
godebug += "," + extraGODEBUG
}
cmd.Env = append(cmd.Env, "GODEBUG="+godebug)
if _, ok := os.LookupEnv("GOTRACEBACK"); !ok {
// Unless overriden, set GOTRACEBACK=crash.
cmd.Env = append(cmd.Env, "GOTRACEBACK=crash")
}
// Capture stdout and stderr.
//

View file

@ -63,9 +63,7 @@ func (h *JSONHandler) WithGroup(name string) Handler {
// Otherwise, the key is "time"
// and the value is output as with json.Marshal.
//
// If the Record's level is zero, the level is omitted.
// Otherwise, the key is "level"
// and the value of [Level.String] is output.
// The level's key is "level" and its value is the result of calling [Level.String].
//
// If the AddSource option is set and source information is available,
// the key is "source", and the value is a record of type [Source].

Some files were not shown because too many files have changed in this diff Show more