Initialize module and dependencies

This commit is contained in:
dwrz
2026-02-13 14:59:42 +00:00
commit 0740968bca
390 changed files with 131652 additions and 0 deletions

27
vendor/golang.org/x/tools/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,27 @@
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/tools/PATENTS generated vendored Normal file
View File

@@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

622
vendor/golang.org/x/tools/cmd/deadcode/deadcode.go generated vendored Normal file
View File

@@ -0,0 +1,622 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
_ "embed"
"encoding/json"
"flag"
"fmt"
"go/ast"
"go/token"
"go/types"
"io"
"log"
"maps"
"os"
"path/filepath"
"regexp"
"runtime"
"runtime/pprof"
"slices"
"sort"
"strings"
"text/template"
"golang.org/x/telemetry"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/rta"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/internal/typesinternal"
)
//go:embed doc.go
var doc string
// flags
var (
testFlag = flag.Bool("test", false, "include implicit test packages and executables")
tagsFlag = flag.String("tags", "", "comma-separated list of extra build tags (see: go help buildconstraint)")
filterFlag = flag.String("filter", "<module>", "report only packages matching this regular expression (default: module of first package)")
generatedFlag = flag.Bool("generated", false, "include dead functions in generated Go files")
whyLiveFlag = flag.String("whylive", "", "show a path from main to the named function")
formatFlag = flag.String("f", "", "format output records using template")
jsonFlag = flag.Bool("json", false, "output JSON records")
cpuProfile = flag.String("cpuprofile", "", "write CPU profile to this file")
memProfile = flag.String("memprofile", "", "write memory profile to this file")
)
func usage() {
// Extract the content of the /* ... */ comment in doc.go.
_, after, _ := strings.Cut(doc, "/*\n")
doc, _, _ := strings.Cut(after, "*/")
io.WriteString(flag.CommandLine.Output(), doc+`
Flags:
`)
flag.PrintDefaults()
}
func main() {
telemetry.Start(telemetry.Config{ReportCrashes: true})
log.SetPrefix("deadcode: ")
log.SetFlags(0) // no time prefix
flag.Usage = usage
flag.Parse()
if len(flag.Args()) == 0 {
usage()
os.Exit(2)
}
if *cpuProfile != "" {
f, err := os.Create(*cpuProfile)
if err != nil {
log.Fatal(err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal(err)
}
// NB: profile won't be written in case of error.
defer pprof.StopCPUProfile()
}
if *memProfile != "" {
f, err := os.Create(*memProfile)
if err != nil {
log.Fatal(err)
}
// NB: profile won't be written in case of error.
defer func() {
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatalf("Writing memory profile: %v", err)
}
f.Close()
}()
}
// Reject bad output options early.
if *formatFlag != "" {
if *jsonFlag {
log.Fatalf("you cannot specify both -f=template and -json")
}
if _, err := template.New("deadcode").Parse(*formatFlag); err != nil {
log.Fatalf("invalid -f: %v", err)
}
}
// Load, parse, and type-check the complete program(s).
cfg := &packages.Config{
BuildFlags: []string{"-tags=" + *tagsFlag},
Mode: packages.LoadAllSyntax | packages.NeedModule,
Tests: *testFlag,
}
initial, err := packages.Load(cfg, flag.Args()...)
if err != nil {
log.Fatalf("Load: %v", err)
}
if len(initial) == 0 {
log.Fatalf("no packages")
}
if packages.PrintErrors(initial) > 0 {
log.Fatalf("packages contain errors")
}
// If -filter is unset, use first module (if available).
if *filterFlag == "<module>" {
seen := make(map[string]bool)
var patterns []string
for _, pkg := range initial {
if pkg.Module != nil && pkg.Module.Path != "" && !seen[pkg.Module.Path] {
seen[pkg.Module.Path] = true
patterns = append(patterns, regexp.QuoteMeta(pkg.Module.Path))
}
}
if patterns != nil {
*filterFlag = "^(" + strings.Join(patterns, "|") + ")\\b"
} else {
*filterFlag = "" // match any
}
}
filter, err := regexp.Compile(*filterFlag)
if err != nil {
log.Fatalf("-filter: %v", err)
}
// Create SSA-form program representation
// and find main packages.
prog, pkgs := ssautil.AllPackages(initial, ssa.InstantiateGenerics)
prog.Build()
mains := ssautil.MainPackages(pkgs)
if len(mains) == 0 {
log.Fatalf("no main packages")
}
var roots []*ssa.Function
for _, main := range mains {
roots = append(roots, main.Func("init"), main.Func("main"))
}
// Gather all source-level functions,
// as the user interface is expressed in terms of them.
//
// We ignore synthetic wrappers, and nested functions. Literal
// functions passed as arguments to other functions are of
// course address-taken and there exists a dynamic call of
// that signature, so when they are unreachable, it is
// invariably because the parent is unreachable.
var (
sourceFuncs []*ssa.Function
generated = make(map[string]bool)
interfaceTypes = make(map[*types.Package][]*types.Interface)
)
packages.Visit(initial, nil, func(p *packages.Package) {
// Collect interfaces by package for marker method identification.
var interfaces []*types.Interface
scope := p.Types.Scope()
for _, name := range scope.Names() {
if typeName, ok := scope.Lookup(name).(*types.TypeName); ok &&
types.IsInterface(typeName.Type()) {
interfaces = append(interfaces, typeName.Type().Underlying().(*types.Interface))
}
}
interfaceTypes[p.Types] = interfaces
for _, file := range p.Syntax {
for _, decl := range file.Decls {
if decl, ok := decl.(*ast.FuncDecl); ok {
obj := p.TypesInfo.Defs[decl.Name].(*types.Func)
fn := prog.FuncValue(obj)
sourceFuncs = append(sourceFuncs, fn)
}
}
if ast.IsGenerated(file) {
generated[p.Fset.File(file.Pos()).Name()] = true
}
}
})
// Compute the reachabilty from main.
// (Build a call graph only for -whylive.)
res := rta.Analyze(roots, *whyLiveFlag != "")
// Subtle: the -test flag causes us to analyze test variants
// such as "package p as compiled for p.test" or even "for q.test".
// This leads to multiple distinct ssa.Function instances that
// represent the same source declaration, and it is essentially
// impossible to discover this from the SSA representation
// (since it has lost the connection to go/packages.Package.ID).
//
// So, we de-duplicate such variants by position:
// if any one of them is live, we consider all of them live.
// (We use Position not Pos to avoid assuming that files common
// to packages "p" and "p [p.test]" were parsed only once.)
reachablePosn := make(map[token.Position]bool)
for fn := range res.Reachable {
if fn.Pos().IsValid() || fn.Name() == "init" {
reachablePosn[prog.Fset.Position(fn.Pos())] = true
}
}
// The -whylive=fn flag causes deadcode to explain why a function
// is not dead, by showing a path to it from some root.
if *whyLiveFlag != "" {
targets := make(map[*ssa.Function]bool)
for _, fn := range sourceFuncs {
if prettyName(fn, true) == *whyLiveFlag {
targets[fn] = true
}
}
if len(targets) == 0 {
// Function is not part of the program.
//
// TODO(adonovan): improve the UX here in case
// of spelling or syntax mistakes. Some ideas:
// - a cmd/callgraph command to enumerate
// available functions.
// - a deadcode -live flag to compute the complement.
// - a syntax hint: example.com/pkg.Func or (example.com/pkg.Type).Method
// - report the element of AllFunctions with the smallest
// Levenshtein distance from *whyLiveFlag.
// - permit -whylive=regexp. But beware of spurious
// matches (e.g. fmt.Print matches fmt.Println)
// and the annoyance of having to quote parens (*T).f.
log.Fatalf("function %q not found in program", *whyLiveFlag)
}
// Opt: remove the unreachable ones.
for fn := range targets {
if !reachablePosn[prog.Fset.Position(fn.Pos())] {
delete(targets, fn)
}
}
if len(targets) == 0 {
log.Fatalf("function %s is dead code", *whyLiveFlag)
}
res.CallGraph.DeleteSyntheticNodes() // inline synthetic wrappers (except inits)
root, path := pathSearch(roots, res, targets)
if root == nil {
// RTA doesn't add callgraph edges for reflective calls.
log.Fatalf("%s is reachable only through reflection", *whyLiveFlag)
}
if len(path) == 0 {
// No edges => one of the targets is a root.
// Rather than (confusingly) print nothing, make this an error.
log.Fatalf("%s is a root", root.Func)
}
// Build a list of jsonEdge records
// to print as -json or -f=template.
var edges []any
for _, edge := range path {
edges = append(edges, jsonEdge{
Initial: cond(len(edges) == 0, prettyName(edge.Caller.Func, true), ""),
Kind: cond(isStaticCall(edge), "static", "dynamic"),
Position: toJSONPosition(prog.Fset.Position(edge.Pos())),
Callee: prettyName(edge.Callee.Func, true),
})
}
format := `{{if .Initial}}{{printf "%19s%s\n" "" .Initial}}{{end}}{{printf "%8s@L%.4d --> %s" .Kind .Position.Line .Callee}}`
if *formatFlag != "" {
format = *formatFlag
}
printObjects(format, edges)
return
}
// Group unreachable functions by package path.
byPkgPath := make(map[string]map[*ssa.Function]bool)
for _, fn := range sourceFuncs {
posn := prog.Fset.Position(fn.Pos())
if !reachablePosn[posn] {
reachablePosn[posn] = true // suppress dups with same pos
pkgpath := fn.Pkg.Pkg.Path()
m, ok := byPkgPath[pkgpath]
if !ok {
m = make(map[*ssa.Function]bool)
byPkgPath[pkgpath] = m
}
m[fn] = true
}
}
// Build array of jsonPackage objects.
var packages []any
for _, pkgpath := range slices.Sorted(maps.Keys(byPkgPath)) {
if !filter.MatchString(pkgpath) {
continue
}
m := byPkgPath[pkgpath]
// Print functions that appear within the same file in
// declaration order. This tends to keep related
// methods such as (T).Marshal and (*T).Unmarshal
// together better than sorting.
fns := slices.Collect(maps.Keys(m))
sort.Slice(fns, func(i, j int) bool {
xposn := prog.Fset.Position(fns[i].Pos())
yposn := prog.Fset.Position(fns[j].Pos())
if xposn.Filename != yposn.Filename {
return xposn.Filename < yposn.Filename
}
return xposn.Line < yposn.Line
})
var functions []jsonFunction
for _, fn := range fns {
posn := prog.Fset.Position(fn.Pos())
// Without -generated, skip functions declared in
// generated Go files.
// (Functions called by them may still be reported.)
gen := generated[posn.Filename]
if gen && !*generatedFlag {
continue
}
// Marker methods should not be reported
marker := isMarkerMethod(fn, interfaceTypes[fn.Pkg.Pkg])
if marker {
continue
}
functions = append(functions, jsonFunction{
Name: prettyName(fn, false),
Position: toJSONPosition(posn),
Generated: gen,
Marker: marker,
})
}
if len(functions) > 0 {
packages = append(packages, jsonPackage{
Name: fns[0].Pkg.Pkg.Name(),
Path: pkgpath,
Funcs: functions,
})
}
}
// Default line-oriented format: "a/b/c.go:1:2: unreachable func: T.f"
format := `{{range .Funcs}}{{printf "%s: unreachable func: %s\n" .Position .Name}}{{end}}`
if *formatFlag != "" {
format = *formatFlag
}
printObjects(format, packages)
}
// prettyName is a fork of Function.String designed to reduce
// go/ssa's fussy punctuation symbols, e.g. "(*pkg.T).F" -> "pkg.T.F".
//
// It only works for functions that remain after
// callgraph.Graph.DeleteSyntheticNodes: source-level named functions
// and methods, their anonymous functions, and synthetic package
// initializers.
func prettyName(fn *ssa.Function, qualified bool) string {
var buf strings.Builder
// optional package qualifier
if qualified && fn.Pkg != nil {
fmt.Fprintf(&buf, "%s.", fn.Pkg.Pkg.Path())
}
var format func(*ssa.Function)
format = func(fn *ssa.Function) {
// anonymous?
if fn.Parent() != nil {
format(fn.Parent())
i := slices.Index(fn.Parent().AnonFuncs, fn)
fmt.Fprintf(&buf, "$%d", i+1)
return
}
// method receiver?
if recv := fn.Signature.Recv(); recv != nil {
_, named := typesinternal.ReceiverNamed(recv)
buf.WriteString(named.Obj().Name())
buf.WriteByte('.')
}
// function/method name
buf.WriteString(fn.Name())
}
format(fn)
return buf.String()
}
// printObjects formats an array of objects, either as JSON or using a
// template, following the manner of 'go list (-json|-f=template)'.
func printObjects(format string, objects []any) {
if *jsonFlag {
out, err := json.MarshalIndent(objects, "", "\t")
if err != nil {
log.Fatalf("internal error: %v", err)
}
os.Stdout.Write(out)
return
}
// -f=template. Parse can't fail: we checked it earlier.
tmpl := template.Must(template.New("deadcode").Parse(format))
for _, object := range objects {
var buf bytes.Buffer
if err := tmpl.Execute(&buf, object); err != nil {
log.Fatal(err)
}
if n := buf.Len(); n == 0 || buf.Bytes()[n-1] != '\n' {
buf.WriteByte('\n')
}
os.Stdout.Write(buf.Bytes())
}
}
// pathSearch returns the shortest path from one of the roots to one
// of the targets (along with the root itself), or zero if no path was found.
func pathSearch(roots []*ssa.Function, res *rta.Result, targets map[*ssa.Function]bool) (*callgraph.Node, []*callgraph.Edge) {
// Search breadth-first (for shortest path) from the root.
//
// We don't use the virtual CallGraph.Root node as we wish to
// choose the order in which we search entrypoints:
// non-test packages before test packages,
// main functions before init functions.
// Sort roots into preferred order.
importsTesting := func(fn *ssa.Function) bool {
isTesting := func(p *types.Package) bool { return p.Path() == "testing" }
return slices.ContainsFunc(fn.Pkg.Pkg.Imports(), isTesting)
}
sort.Slice(roots, func(i, j int) bool {
x, y := roots[i], roots[j]
xtest := importsTesting(x)
ytest := importsTesting(y)
if xtest != ytest {
return !xtest // non-tests before tests
}
xinit := x.Name() == "init"
yinit := y.Name() == "init"
if xinit != yinit {
return !xinit // mains before inits
}
return false
})
search := func(allowDynamic bool) (*callgraph.Node, []*callgraph.Edge) {
// seen maps each encountered node to its predecessor on the
// path to a root node, or to nil for root itself.
seen := make(map[*callgraph.Node]*callgraph.Edge)
bfs := func(root *callgraph.Node) []*callgraph.Edge {
queue := []*callgraph.Node{root}
seen[root] = nil
for len(queue) > 0 {
node := queue[0]
queue = queue[1:]
// found a path?
if targets[node.Func] {
path := []*callgraph.Edge{} // non-nil in case len(path)=0
for {
edge := seen[node]
if edge == nil {
slices.Reverse(path)
return path
}
path = append(path, edge)
node = edge.Caller
}
}
for _, edge := range node.Out {
if allowDynamic || isStaticCall(edge) {
if _, ok := seen[edge.Callee]; !ok {
seen[edge.Callee] = edge
queue = append(queue, edge.Callee)
}
}
}
}
return nil
}
for _, rootFn := range roots {
root := res.CallGraph.Nodes[rootFn]
if root == nil {
// Missing call graph node for root.
// TODO(adonovan): seems like a bug in rta.
continue
}
if path := bfs(root); path != nil {
return root, path
}
}
return nil, nil
}
for _, allowDynamic := range []bool{false, true} {
if root, path := search(allowDynamic); path != nil {
return root, path
}
}
return nil, nil
}
// -- utilities --
func isStaticCall(edge *callgraph.Edge) bool {
return edge.Site != nil && edge.Site.Common().StaticCallee() != nil
}
var cwd, _ = os.Getwd()
func toJSONPosition(posn token.Position) jsonPosition {
// Use cwd-relative filename if possible.
filename := posn.Filename
if rel, err := filepath.Rel(cwd, filename); err == nil && !strings.HasPrefix(rel, "..") {
filename = rel
}
return jsonPosition{filename, posn.Line, posn.Column}
}
func cond[T any](cond bool, t, f T) T {
if cond {
return t
} else {
return f
}
}
// isMarkerMethod reports whether fn is a marker method:
// an unexported, empty-bodied method with no parameters or results
// that implements some named interface type in the same package.
func isMarkerMethod(fn *ssa.Function, interfaceTypes []*types.Interface) bool {
// Is it an unexported method of no params/results?
if !(fn.Signature.Recv() != nil &&
!ast.IsExported(fn.Name()) &&
fn.Signature.Params() == nil &&
fn.Signature.Results() == nil) {
return false
}
// Does the method have an empty body?
body := fn.Syntax().(*ast.FuncDecl).Body
if body == nil || len(body.List) > 0 {
return false
}
// Does it implement some named interface type in this package?
return slices.ContainsFunc(interfaceTypes, func(iface *types.Interface) bool {
return types.Implements(fn.Signature.Recv().Type(), iface)
})
}
// -- output protocol (for JSON or text/template) --
// Keep in sync with doc comment!
type jsonFunction struct {
Name string // name (sans package qualifier)
Position jsonPosition // file/line/column of declaration
Generated bool // function is declared in a generated .go file
Marker bool // function is a marker interface method
}
func (f jsonFunction) String() string { return f.Name }
type jsonPackage struct {
Name string // declared name
Path string // full import path
Funcs []jsonFunction // non-empty list of package's dead functions
}
func (p jsonPackage) String() string { return p.Path }
// The Initial and Callee names are package-qualified.
type jsonEdge struct {
Initial string `json:",omitempty"` // initial entrypoint (main or init); first edge only
Kind string // = static | dynamic
Position jsonPosition
Callee string
}
type jsonPosition struct {
File string
Line, Col int
}
func (p jsonPosition) String() string {
return fmt.Sprintf("%s:%d:%d", p.File, p.Line, p.Col)
}

146
vendor/golang.org/x/tools/cmd/deadcode/doc.go generated vendored Normal file
View File

@@ -0,0 +1,146 @@
// 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.
/*
The deadcode command reports unreachable functions in Go programs.
Usage: deadcode [flags] package...
The deadcode command loads a Go program from source then uses Rapid
Type Analysis (RTA) to build a call graph of all the functions
reachable from the program's main function. Any functions that are not
reachable are reported as dead code, grouped by package.
Packages are expressed in the notation of 'go list' (or other
underlying build system if you are using an alternative
golang.org/x/go/packages driver). Only executable (main) packages are
considered starting points for the analysis.
The -test flag causes it to analyze test executables too. Tests
sometimes make use of functions that would otherwise appear to be dead
code, and public API functions reported as dead with -test indicate
possible gaps in your test coverage. Bear in mind that an Example test
function without an "Output:" comment is merely documentation:
it is dead code, and does not contribute coverage.
The -filter flag restricts results to packages that match the provided
regular expression; its default value matches the listed packages and any other
packages belonging to the same modules. Use -filter= to display all results.
Example: show all dead code within the gopls module:
$ deadcode -test golang.org/x/tools/gopls/...
The analysis can soundly analyze dynamic calls though func values,
interface methods, and reflection. However, it does not currently
understand the aliasing created by //go:linkname directives, so it
will fail to recognize that calls to a linkname-annotated function
with no body in fact dispatch to the function named in the annotation.
This may result in the latter function being spuriously reported as dead.
By default, the tool does not report dead functions in generated files,
as determined by the special comment described in
https://go.dev/s/generatedcode. Use the -generated flag to include them.
The tool also does not report marker interface methods by default.
Marker interface methods are typically used to create compile-time constraints
to ensure that only specific types can implement a particular interface.
These methods have no other functionality (empty function body) and are never invoked.
Although marker interface methods are technically unreachable, removing them would break
the interface implementation. Hence, the tool excludes them from the report.
In any case, just because a function is reported as dead does not mean
it is unconditionally safe to delete it. For example, a dead function
may be referenced by another dead function, and a dead method may be
required to satisfy an interface that is never called.
Some judgement is required.
The analysis is valid only for a single GOOS/GOARCH/-tags configuration,
so a function reported as dead may be live in a different configuration.
Consider running the tool once for each configuration of interest.
Consider using a line-oriented output format (see below) to make it
easier to compute the intersection of results across all runs.
# Output
The command supports three output formats.
With no flags, the command prints the name and location of each dead
function in the form of a typical compiler diagnostic, for example:
$ deadcode -f='{{range .Funcs}}{{println .Position}}{{end}}' -test ./gopls/...
gopls/internal/protocol/command.go:1206:6: unreachable func: openClientEditor
gopls/internal/template/parse.go:414:18: unreachable func: Parsed.WriteNode
gopls/internal/template/parse.go:419:18: unreachable func: wrNode.writeNode
With the -json flag, the command prints an array of Package
objects, as defined by the JSON schema (see below).
With the -f=template flag, the command executes the specified template
on each Package record. So, this template shows dead functions grouped
by package:
$ deadcode -f='{{println .Path}}{{range .Funcs}}{{printf "\t%s\n" .Name}}{{end}}{{println}}' -test ./gopls/...
golang.org/x/tools/gopls/internal/lsp
openClientEditor
golang.org/x/tools/gopls/internal/template
Parsed.WriteNode
wrNode.writeNode
# Why is a function not dead?
The -whylive=function flag explain why the named function is not dead
by showing an arbitrary shortest path to it from one of the main functions.
(To enumerate the functions in a program, or for more sophisticated
call graph queries, use golang.org/x/tools/cmd/callgraph.)
Fully static call paths are preferred over paths involving dynamic
edges, even if longer. Paths starting from a non-test package are
preferred over those from tests. Paths from main functions are
preferred over paths from init functions.
The result is a list of Edge objects (see JSON schema below).
Again, the -json and -f=template flags may be used to control
the formatting of the list of Edge objects.
The default format shows, for each edge in the path, whether the call
is static or dynamic, and its source line number. For example:
$ deadcode -whylive=bytes.Buffer.String -test ./cmd/deadcode/...
golang.org/x/tools/cmd/deadcode.main
static@L0117 --> golang.org/x/tools/go/packages.Load
static@L0262 --> golang.org/x/tools/go/packages.defaultDriver
static@L0305 --> golang.org/x/tools/go/packages.goListDriver
static@L0153 --> golang.org/x/tools/go/packages.goListDriver$1
static@L0154 --> golang.org/x/tools/go/internal/packagesdriver.GetSizesForArgsGolist
static@L0044 --> bytes.Buffer.String
# JSON schema
type Package struct {
Name string // declared name
Path string // full import path
Funcs []Function // list of dead functions within it
}
type Function struct {
Name string // name (sans package qualifier)
Position Position // file/line/column of function declaration
Generated bool // function is declared in a generated .go file
Marker bool // function is a marker interface method
}
type Edge struct {
Initial string // initial entrypoint (main or init); first edge only
Kind string // = static | dynamic
Position Position // file/line/column of call site
Callee string // target of the call
}
type Position struct {
File string // name of file
Line, Col int // line and byte index, both 1-based
}
*/
package main

50
vendor/golang.org/x/tools/cmd/goimports/doc.go generated vendored Normal file
View File

@@ -0,0 +1,50 @@
// Copyright 2013 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.
/*
Command goimports updates your Go import lines,
adding missing ones and removing unreferenced ones.
$ go install golang.org/x/tools/cmd/goimports@latest
In addition to fixing imports, goimports also formats
your code in the same style as gofmt so it can be used
as a replacement for your editor's gofmt-on-save hook.
For emacs, make sure you have the latest go-mode.el:
https://github.com/dominikh/go-mode.el
Then in your .emacs file:
(setq gofmt-command "goimports")
(add-hook 'before-save-hook 'gofmt-before-save)
For vim, set "gofmt_command" to "goimports":
https://golang.org/change/39c724dd7f252
https://golang.org/wiki/IDEsAndTextEditorPlugins
etc
For GoSublime, follow the steps described here:
http://michaelwhatcott.com/gosublime-goimports/
For other editors, you probably know what to do.
To exclude directories in your $GOPATH from being scanned for Go
files, goimports respects a configuration file at
$GOPATH/src/.goimportsignore which may contain blank lines, comment
lines (beginning with '#'), or lines naming a directory relative to
the configuration file to ignore when scanning. No globbing or regex
patterns are allowed. Use the "-v" verbose flag to verify it's
working and see what goimports is doing.
File bugs or feature requests at:
https://golang.org/issues/new?title=x/tools/cmd/goimports:+
Happy hacking!
*/
package main // import "golang.org/x/tools/cmd/goimports"

383
vendor/golang.org/x/tools/cmd/goimports/goimports.go generated vendored Normal file
View File

@@ -0,0 +1,383 @@
// Copyright 2013 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 main
import (
"bufio"
"bytes"
"errors"
"flag"
"fmt"
"go/scanner"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"runtime/pprof"
"strings"
"golang.org/x/telemetry/counter"
"golang.org/x/tools/internal/gocommand"
"golang.org/x/tools/internal/imports"
)
var (
// main operation modes
list = flag.Bool("l", false, "list files whose formatting differs from goimport's")
write = flag.Bool("w", false, "write result to (source) file instead of stdout")
doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
srcdir = flag.String("srcdir", "", "choose imports as if source code is from `dir`. When operating on a single file, dir may instead be the complete file name.")
verbose bool // verbose logging
cpuProfile = flag.String("cpuprofile", "", "CPU profile output")
memProfile = flag.String("memprofile", "", "memory profile output")
memProfileRate = flag.Int("memrate", 0, "if > 0, sets runtime.MemProfileRate")
options = &imports.Options{
TabWidth: 8,
TabIndent: true,
Comments: true,
Fragment: true,
Env: &imports.ProcessEnv{
GocmdRunner: &gocommand.Runner{},
},
}
exitCode = 0
)
func init() {
flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
flag.StringVar(&options.LocalPrefix, "local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list")
flag.BoolVar(&options.FormatOnly, "format-only", false, "if true, don't fix imports and only format. In this mode, goimports is effectively gofmt, with the addition that imports are grouped into sections.")
}
func report(err error) {
scanner.PrintError(os.Stderr, err)
exitCode = 2
}
func usage() {
fmt.Fprintf(os.Stderr, "usage: goimports [flags] [path ...]\n")
flag.PrintDefaults()
os.Exit(2)
}
func isGoFile(f os.FileInfo) bool {
// ignore non-Go files
name := f.Name()
return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}
// argumentType is which mode goimports was invoked as.
type argumentType int
const (
// fromStdin means the user is piping their source into goimports.
fromStdin argumentType = iota
// singleArg is the common case from editors, when goimports is run on
// a single file.
singleArg
// multipleArg is when the user ran "goimports file1.go file2.go"
// or ran goimports on a directory tree.
multipleArg
)
func processFile(filename string, in io.Reader, out io.Writer, argType argumentType) error {
opt := options
if argType == fromStdin {
nopt := *options
nopt.Fragment = true
opt = &nopt
}
if in == nil {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
in = f
}
src, err := io.ReadAll(in)
if err != nil {
return err
}
target := filename
if *srcdir != "" {
// Determine whether the provided -srcdirc is a directory or file
// and then use it to override the target.
//
// See https://github.com/dominikh/go-mode.el/issues/146
if isFile(*srcdir) {
if argType == multipleArg {
return errors.New("-srcdir value can't be a file when passing multiple arguments or when walking directories")
}
target = *srcdir
} else if argType == singleArg && strings.HasSuffix(*srcdir, ".go") && !isDir(*srcdir) {
// For a file which doesn't exist on disk yet, but might shortly.
// e.g. user in editor opens $DIR/newfile.go and newfile.go doesn't yet exist on disk.
// The goimports on-save hook writes the buffer to a temp file
// first and runs goimports before the actual save to newfile.go.
// The editor's buffer is named "newfile.go" so that is passed to goimports as:
// goimports -srcdir=/gopath/src/pkg/newfile.go /tmp/gofmtXXXXXXXX.go
// and then the editor reloads the result from the tmp file and writes
// it to newfile.go.
target = *srcdir
} else {
// Pretend that file is from *srcdir in order to decide
// visible imports correctly.
target = filepath.Join(*srcdir, filepath.Base(filename))
}
}
res, err := imports.Process(target, src, opt)
if err != nil {
return err
}
if !bytes.Equal(src, res) {
// formatting has changed
if *list {
fmt.Fprintln(out, filename)
}
if *write {
if argType == fromStdin {
// filename is "<standard input>"
return errors.New("can't use -w on stdin")
}
// On Windows, we need to re-set the permissions from the file. See golang/go#38225.
var perms os.FileMode
if fi, err := os.Stat(filename); err == nil {
perms = fi.Mode() & os.ModePerm
}
err = os.WriteFile(filename, res, perms)
if err != nil {
return err
}
}
if *doDiff {
if argType == fromStdin {
filename = "stdin.go" // because <standard input>.orig looks silly
}
data, err := diff(src, res, filename)
if err != nil {
return fmt.Errorf("computing diff: %s", err)
}
fmt.Printf("diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename))
out.Write(data)
}
}
if !*list && !*write && !*doDiff {
_, err = out.Write(res)
}
return err
}
func visitFile(path string, f os.FileInfo, err error) error {
if err == nil && isGoFile(f) {
err = processFile(path, nil, os.Stdout, multipleArg)
}
if err != nil {
report(err)
}
return nil
}
func walkDir(path string) {
filepath.Walk(path, visitFile)
}
func main() {
// is anyone using this command?
counter.Open()
counter.Inc("tools/cmd:goimports")
runtime.GOMAXPROCS(runtime.NumCPU())
// call gofmtMain in a separate function
// so that it can use defer and have them
// run before the exit.
gofmtMain()
os.Exit(exitCode)
}
// parseFlags parses command line flags and returns the paths to process.
// It's a var so that custom implementations can replace it in other files.
var parseFlags = func() []string {
flag.BoolVar(&verbose, "v", false, "verbose logging")
flag.Parse()
return flag.Args()
}
func bufferedFileWriter(dest string) (w io.Writer, close func()) {
f, err := os.Create(dest)
if err != nil {
log.Fatal(err)
}
bw := bufio.NewWriter(f)
return bw, func() {
if err := bw.Flush(); err != nil {
log.Fatalf("error flushing %v: %v", dest, err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
}
}
func gofmtMain() {
flag.Usage = usage
paths := parseFlags()
if *cpuProfile != "" {
bw, flush := bufferedFileWriter(*cpuProfile)
pprof.StartCPUProfile(bw)
defer flush()
defer pprof.StopCPUProfile()
}
// doTrace is a conditionally compiled wrapper around runtime/trace. It is
// used to allow goimports to compile under gccgo, which does not support
// runtime/trace. See https://golang.org/issue/15544.
defer doTrace()()
if *memProfileRate > 0 {
runtime.MemProfileRate = *memProfileRate
bw, flush := bufferedFileWriter(*memProfile)
defer func() {
runtime.GC() // materialize all statistics
if err := pprof.WriteHeapProfile(bw); err != nil {
log.Fatal(err)
}
flush()
}()
}
if verbose {
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
options.Env.Logf = log.Printf
}
if options.TabWidth < 0 {
fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", options.TabWidth)
exitCode = 2
return
}
if len(paths) == 0 {
if err := processFile("<standard input>", os.Stdin, os.Stdout, fromStdin); err != nil {
report(err)
}
return
}
argType := singleArg
if len(paths) > 1 {
argType = multipleArg
}
for _, path := range paths {
switch dir, err := os.Stat(path); {
case err != nil:
report(err)
case dir.IsDir():
walkDir(path)
default:
if err := processFile(path, nil, os.Stdout, argType); err != nil {
report(err)
}
}
}
}
func writeTempFile(dir, prefix string, data []byte) (string, error) {
file, err := os.CreateTemp(dir, prefix)
if err != nil {
return "", err
}
_, err = file.Write(data)
if err1 := file.Close(); err == nil {
err = err1
}
if err != nil {
os.Remove(file.Name())
return "", err
}
return file.Name(), nil
}
func diff(b1, b2 []byte, filename string) (data []byte, err error) {
f1, err := writeTempFile("", "gofmt", b1)
if err != nil {
return
}
defer os.Remove(f1)
f2, err := writeTempFile("", "gofmt", b2)
if err != nil {
return
}
defer os.Remove(f2)
cmd := "diff"
if runtime.GOOS == "plan9" {
cmd = "/bin/ape/diff"
}
data, err = exec.Command(cmd, "-u", f1, f2).CombinedOutput()
if len(data) > 0 {
// diff exits with a non-zero status when the files don't match.
// Ignore that failure as long as we get output.
return replaceTempFilename(data, filename)
}
return
}
// replaceTempFilename replaces temporary filenames in diff with actual one.
//
// --- /tmp/gofmt316145376 2017-02-03 19:13:00.280468375 -0500
// +++ /tmp/gofmt617882815 2017-02-03 19:13:00.280468375 -0500
// ...
// ->
// --- path/to/file.go.orig 2017-02-03 19:13:00.280468375 -0500
// +++ path/to/file.go 2017-02-03 19:13:00.280468375 -0500
// ...
func replaceTempFilename(diff []byte, filename string) ([]byte, error) {
bs := bytes.SplitN(diff, []byte{'\n'}, 3)
if len(bs) < 3 {
return nil, fmt.Errorf("got unexpected diff for %s", filename)
}
// Preserve timestamps.
var t0, t1 []byte
if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 {
t0 = bs[0][i:]
}
if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 {
t1 = bs[1][i:]
}
// Always print filepath with slash separator.
f := filepath.ToSlash(filename)
bs[0] = fmt.Appendf(nil, "--- %s%s", f+".orig", t0)
bs[1] = fmt.Appendf(nil, "+++ %s%s", f, t1)
return bytes.Join(bs, []byte{'\n'}), nil
}
// isFile reports whether name is a file.
func isFile(name string) bool {
fi, err := os.Stat(name)
return err == nil && fi.Mode().IsRegular()
}
// isDir reports whether name is a directory.
func isDir(name string) bool {
fi, err := os.Stat(name)
return err == nil && fi.IsDir()
}

View File

@@ -0,0 +1,26 @@
// Copyright 2016 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 gc
package main
import (
"flag"
"runtime/trace"
)
var traceProfile = flag.String("trace", "", "trace profile output")
func doTrace() func() {
if *traceProfile != "" {
bw, flush := bufferedFileWriter(*traceProfile)
trace.Start(bw)
return func() {
trace.Stop()
flush()
}
}
return func() {}
}

View File

@@ -0,0 +1,11 @@
// Copyright 2016 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 !gc
package main
func doTrace() func() {
return func() {}
}

663
vendor/golang.org/x/tools/go/ast/astutil/enclosing.go generated vendored Normal file
View File

@@ -0,0 +1,663 @@
// Copyright 2013 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 astutil
// This file defines utilities for working with source positions.
import (
"fmt"
"go/ast"
"go/token"
"sort"
)
// PathEnclosingInterval returns the node that encloses the source
// interval [start, end), and all its ancestors up to the AST root.
//
// The definition of "enclosing" used by this function considers
// additional whitespace abutting a node to be enclosed by it.
// In this example:
//
// z := x + y // add them
// <-A->
// <----B----->
//
// the ast.BinaryExpr(+) node is considered to enclose interval B
// even though its [Pos()..End()) is actually only interval A.
// This behaviour makes user interfaces more tolerant of imperfect
// input.
//
// This function treats tokens as nodes, though they are not included
// in the result. e.g. PathEnclosingInterval("+") returns the
// enclosing ast.BinaryExpr("x + y").
//
// If start==end, the 1-char interval following start is used instead.
//
// The 'exact' result is true if the interval contains only path[0]
// and perhaps some adjacent whitespace. It is false if the interval
// overlaps multiple children of path[0], or if it contains only
// interior whitespace of path[0].
// In this example:
//
// z := x + y // add them
// <--C--> <---E-->
// ^
// D
//
// intervals C, D and E are inexact. C is contained by the
// z-assignment statement, because it spans three of its children (:=,
// x, +). So too is the 1-char interval D, because it contains only
// interior whitespace of the assignment. E is considered interior
// whitespace of the BlockStmt containing the assignment.
//
// The resulting path is never empty; it always contains at least the
// 'root' *ast.File. Ideally PathEnclosingInterval would reject
// intervals that lie wholly or partially outside the range of the
// file, but unfortunately ast.File records only the token.Pos of
// the 'package' keyword, but not of the start of the file itself.
func PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Node, exact bool) {
// fmt.Printf("EnclosingInterval %d %d\n", start, end) // debugging
// Precondition: node.[Pos..End) and adjoining whitespace contain [start, end).
var visit func(node ast.Node) bool
visit = func(node ast.Node) bool {
path = append(path, node)
nodePos := node.Pos()
nodeEnd := node.End()
// fmt.Printf("visit(%T, %d, %d)\n", node, nodePos, nodeEnd) // debugging
// Intersect [start, end) with interval of node.
if start < nodePos {
start = nodePos
}
if end > nodeEnd {
end = nodeEnd
}
// Find sole child that contains [start, end).
children := childrenOf(node)
l := len(children)
for i, child := range children {
// [childPos, childEnd) is unaugmented interval of child.
childPos := child.Pos()
childEnd := child.End()
// [augPos, augEnd) is whitespace-augmented interval of child.
augPos := childPos
augEnd := childEnd
if i > 0 {
augPos = children[i-1].End() // start of preceding whitespace
}
if i < l-1 {
nextChildPos := children[i+1].Pos()
// Does [start, end) lie between child and next child?
if start >= augEnd && end <= nextChildPos {
return false // inexact match
}
augEnd = nextChildPos // end of following whitespace
}
// fmt.Printf("\tchild %d: [%d..%d)\tcontains interval [%d..%d)?\n",
// i, augPos, augEnd, start, end) // debugging
// Does augmented child strictly contain [start, end)?
if augPos <= start && end <= augEnd {
if is[tokenNode](child) {
return true
}
// childrenOf elides the FuncType node beneath FuncDecl.
// Add it back here for TypeParams, Params, Results,
// all FieldLists). But we don't add it back for the "func" token
// even though it is the tree at FuncDecl.Type.Func.
if decl, ok := node.(*ast.FuncDecl); ok {
if fields, ok := child.(*ast.FieldList); ok && fields != decl.Recv {
path = append(path, decl.Type)
}
}
return visit(child)
}
// Does [start, end) overlap multiple children?
// i.e. left-augmented child contains start
// but LR-augmented child does not contain end.
if start < childEnd && end > augEnd {
break
}
}
// No single child contained [start, end),
// so node is the result. Is it exact?
// (It's tempting to put this condition before the
// child loop, but it gives the wrong result in the
// case where a node (e.g. ExprStmt) and its sole
// child have equal intervals.)
if start == nodePos && end == nodeEnd {
return true // exact match
}
return false // inexact: overlaps multiple children
}
// Ensure [start,end) is nondecreasing.
if start > end {
start, end = end, start
}
if start < root.End() && end > root.Pos() {
if start == end {
end = start + 1 // empty interval => interval of size 1
}
exact = visit(root)
// Reverse the path:
for i, l := 0, len(path); i < l/2; i++ {
path[i], path[l-1-i] = path[l-1-i], path[i]
}
} else {
// Selection lies within whitespace preceding the
// first (or following the last) declaration in the file.
// The result nonetheless always includes the ast.File.
path = append(path, root)
}
return
}
// tokenNode is a dummy implementation of ast.Node for a single token.
// They are used transiently by PathEnclosingInterval but never escape
// this package.
type tokenNode struct {
pos token.Pos
end token.Pos
}
func (n tokenNode) Pos() token.Pos {
return n.pos
}
func (n tokenNode) End() token.Pos {
return n.end
}
func tok(pos token.Pos, len int) ast.Node {
return tokenNode{pos, pos + token.Pos(len)}
}
// childrenOf returns the direct non-nil children of ast.Node n.
// It may include fake ast.Node implementations for bare tokens.
// it is not safe to call (e.g.) ast.Walk on such nodes.
func childrenOf(n ast.Node) []ast.Node {
var children []ast.Node
// First add nodes for all true subtrees.
ast.Inspect(n, func(node ast.Node) bool {
if node == n { // push n
return true // recur
}
if node != nil { // push child
children = append(children, node)
}
return false // no recursion
})
// TODO(adonovan): be more careful about missing (!Pos.Valid)
// tokens in trees produced from invalid input.
// Then add fake Nodes for bare tokens.
switch n := n.(type) {
case *ast.ArrayType:
children = append(children,
tok(n.Lbrack, len("[")),
tok(n.Elt.End(), len("]")))
case *ast.AssignStmt:
children = append(children,
tok(n.TokPos, len(n.Tok.String())))
case *ast.BasicLit:
children = append(children,
tok(n.ValuePos, len(n.Value)))
case *ast.BinaryExpr:
children = append(children, tok(n.OpPos, len(n.Op.String())))
case *ast.BlockStmt:
if n.Lbrace.IsValid() {
children = append(children, tok(n.Lbrace, len("{")))
}
if n.Rbrace.IsValid() {
children = append(children, tok(n.Rbrace, len("}")))
}
case *ast.BranchStmt:
children = append(children,
tok(n.TokPos, len(n.Tok.String())))
case *ast.CallExpr:
children = append(children,
tok(n.Lparen, len("(")),
tok(n.Rparen, len(")")))
if n.Ellipsis != 0 {
children = append(children, tok(n.Ellipsis, len("...")))
}
case *ast.CaseClause:
if n.List == nil {
children = append(children,
tok(n.Case, len("default")))
} else {
children = append(children,
tok(n.Case, len("case")))
}
children = append(children, tok(n.Colon, len(":")))
case *ast.ChanType:
switch n.Dir {
case ast.RECV:
children = append(children, tok(n.Begin, len("<-chan")))
case ast.SEND:
children = append(children, tok(n.Begin, len("chan<-")))
case ast.RECV | ast.SEND:
children = append(children, tok(n.Begin, len("chan")))
}
case *ast.CommClause:
if n.Comm == nil {
children = append(children,
tok(n.Case, len("default")))
} else {
children = append(children,
tok(n.Case, len("case")))
}
children = append(children, tok(n.Colon, len(":")))
case *ast.Comment:
// nop
case *ast.CommentGroup:
// nop
case *ast.CompositeLit:
children = append(children,
tok(n.Lbrace, len("{")),
tok(n.Rbrace, len("{")))
case *ast.DeclStmt:
// nop
case *ast.DeferStmt:
children = append(children,
tok(n.Defer, len("defer")))
case *ast.Ellipsis:
children = append(children,
tok(n.Ellipsis, len("...")))
case *ast.EmptyStmt:
// nop
case *ast.ExprStmt:
// nop
case *ast.Field:
// TODO(adonovan): Field.{Doc,Comment,Tag}?
case *ast.FieldList:
if n.Opening.IsValid() {
children = append(children, tok(n.Opening, len("(")))
}
if n.Closing.IsValid() {
children = append(children, tok(n.Closing, len(")")))
}
case *ast.File:
// TODO test: Doc
children = append(children,
tok(n.Package, len("package")))
case *ast.ForStmt:
children = append(children,
tok(n.For, len("for")))
case *ast.FuncDecl:
// TODO(adonovan): FuncDecl.Comment?
// Uniquely, FuncDecl breaks the invariant that
// preorder traversal yields tokens in lexical order:
// in fact, FuncDecl.Recv precedes FuncDecl.Type.Func.
//
// As a workaround, we inline the case for FuncType
// here and order things correctly.
// We also need to insert the elided FuncType just
// before the 'visit' recursion.
//
children = nil // discard ast.Walk(FuncDecl) info subtrees
children = append(children, tok(n.Type.Func, len("func")))
if n.Recv != nil {
children = append(children, n.Recv)
}
children = append(children, n.Name)
if tparams := n.Type.TypeParams; tparams != nil {
children = append(children, tparams)
}
if n.Type.Params != nil {
children = append(children, n.Type.Params)
}
if n.Type.Results != nil {
children = append(children, n.Type.Results)
}
if n.Body != nil {
children = append(children, n.Body)
}
case *ast.FuncLit:
// nop
case *ast.FuncType:
if n.Func != 0 {
children = append(children,
tok(n.Func, len("func")))
}
case *ast.GenDecl:
children = append(children,
tok(n.TokPos, len(n.Tok.String())))
if n.Lparen != 0 {
children = append(children,
tok(n.Lparen, len("(")),
tok(n.Rparen, len(")")))
}
case *ast.GoStmt:
children = append(children,
tok(n.Go, len("go")))
case *ast.Ident:
children = append(children,
tok(n.NamePos, len(n.Name)))
case *ast.IfStmt:
children = append(children,
tok(n.If, len("if")))
case *ast.ImportSpec:
// TODO(adonovan): ImportSpec.{Doc,EndPos}?
case *ast.IncDecStmt:
children = append(children,
tok(n.TokPos, len(n.Tok.String())))
case *ast.IndexExpr:
children = append(children,
tok(n.Lbrack, len("[")),
tok(n.Rbrack, len("]")))
case *ast.IndexListExpr:
children = append(children,
tok(n.Lbrack, len("[")),
tok(n.Rbrack, len("]")))
case *ast.InterfaceType:
children = append(children,
tok(n.Interface, len("interface")))
case *ast.KeyValueExpr:
children = append(children,
tok(n.Colon, len(":")))
case *ast.LabeledStmt:
children = append(children,
tok(n.Colon, len(":")))
case *ast.MapType:
children = append(children,
tok(n.Map, len("map")))
case *ast.ParenExpr:
children = append(children,
tok(n.Lparen, len("(")),
tok(n.Rparen, len(")")))
case *ast.RangeStmt:
children = append(children,
tok(n.For, len("for")),
tok(n.TokPos, len(n.Tok.String())))
case *ast.ReturnStmt:
children = append(children,
tok(n.Return, len("return")))
case *ast.SelectStmt:
children = append(children,
tok(n.Select, len("select")))
case *ast.SelectorExpr:
// nop
case *ast.SendStmt:
children = append(children,
tok(n.Arrow, len("<-")))
case *ast.SliceExpr:
children = append(children,
tok(n.Lbrack, len("[")),
tok(n.Rbrack, len("]")))
case *ast.StarExpr:
children = append(children, tok(n.Star, len("*")))
case *ast.StructType:
children = append(children, tok(n.Struct, len("struct")))
case *ast.SwitchStmt:
children = append(children, tok(n.Switch, len("switch")))
case *ast.TypeAssertExpr:
children = append(children,
tok(n.Lparen-1, len(".")),
tok(n.Lparen, len("(")),
tok(n.Rparen, len(")")))
case *ast.TypeSpec:
// TODO(adonovan): TypeSpec.{Doc,Comment}?
case *ast.TypeSwitchStmt:
children = append(children, tok(n.Switch, len("switch")))
case *ast.UnaryExpr:
children = append(children, tok(n.OpPos, len(n.Op.String())))
case *ast.ValueSpec:
// TODO(adonovan): ValueSpec.{Doc,Comment}?
case *ast.BadDecl, *ast.BadExpr, *ast.BadStmt:
// nop
}
// TODO(adonovan): opt: merge the logic of ast.Inspect() into
// the switch above so we can make interleaved callbacks for
// both Nodes and Tokens in the right order and avoid the need
// to sort.
sort.Sort(byPos(children))
return children
}
type byPos []ast.Node
func (sl byPos) Len() int {
return len(sl)
}
func (sl byPos) Less(i, j int) bool {
return sl[i].Pos() < sl[j].Pos()
}
func (sl byPos) Swap(i, j int) {
sl[i], sl[j] = sl[j], sl[i]
}
// NodeDescription returns a description of the concrete type of n suitable
// for a user interface.
//
// TODO(adonovan): in some cases (e.g. Field, FieldList, Ident,
// StarExpr) we could be much more specific given the path to the AST
// root. Perhaps we should do that.
func NodeDescription(n ast.Node) string {
switch n := n.(type) {
case *ast.ArrayType:
return "array type"
case *ast.AssignStmt:
return "assignment"
case *ast.BadDecl:
return "bad declaration"
case *ast.BadExpr:
return "bad expression"
case *ast.BadStmt:
return "bad statement"
case *ast.BasicLit:
return "basic literal"
case *ast.BinaryExpr:
return fmt.Sprintf("binary %s operation", n.Op)
case *ast.BlockStmt:
return "block"
case *ast.BranchStmt:
switch n.Tok {
case token.BREAK:
return "break statement"
case token.CONTINUE:
return "continue statement"
case token.GOTO:
return "goto statement"
case token.FALLTHROUGH:
return "fall-through statement"
}
case *ast.CallExpr:
if len(n.Args) == 1 && !n.Ellipsis.IsValid() {
return "function call (or conversion)"
}
return "function call"
case *ast.CaseClause:
return "case clause"
case *ast.ChanType:
return "channel type"
case *ast.CommClause:
return "communication clause"
case *ast.Comment:
return "comment"
case *ast.CommentGroup:
return "comment group"
case *ast.CompositeLit:
return "composite literal"
case *ast.DeclStmt:
return NodeDescription(n.Decl) + " statement"
case *ast.DeferStmt:
return "defer statement"
case *ast.Ellipsis:
return "ellipsis"
case *ast.EmptyStmt:
return "empty statement"
case *ast.ExprStmt:
return "expression statement"
case *ast.Field:
// Can be any of these:
// struct {x, y int} -- struct field(s)
// struct {T} -- anon struct field
// interface {I} -- interface embedding
// interface {f()} -- interface method
// func (A) func(B) C -- receiver, param(s), result(s)
return "field/method/parameter"
case *ast.FieldList:
return "field/method/parameter list"
case *ast.File:
return "source file"
case *ast.ForStmt:
return "for loop"
case *ast.FuncDecl:
return "function declaration"
case *ast.FuncLit:
return "function literal"
case *ast.FuncType:
return "function type"
case *ast.GenDecl:
switch n.Tok {
case token.IMPORT:
return "import declaration"
case token.CONST:
return "constant declaration"
case token.TYPE:
return "type declaration"
case token.VAR:
return "variable declaration"
}
case *ast.GoStmt:
return "go statement"
case *ast.Ident:
return "identifier"
case *ast.IfStmt:
return "if statement"
case *ast.ImportSpec:
return "import specification"
case *ast.IncDecStmt:
if n.Tok == token.INC {
return "increment statement"
}
return "decrement statement"
case *ast.IndexExpr:
return "index expression"
case *ast.IndexListExpr:
return "index list expression"
case *ast.InterfaceType:
return "interface type"
case *ast.KeyValueExpr:
return "key/value association"
case *ast.LabeledStmt:
return "statement label"
case *ast.MapType:
return "map type"
case *ast.Package:
return "package"
case *ast.ParenExpr:
return "parenthesized " + NodeDescription(n.X)
case *ast.RangeStmt:
return "range loop"
case *ast.ReturnStmt:
return "return statement"
case *ast.SelectStmt:
return "select statement"
case *ast.SelectorExpr:
return "selector"
case *ast.SendStmt:
return "channel send"
case *ast.SliceExpr:
return "slice expression"
case *ast.StarExpr:
return "*-operation" // load/store expr or pointer type
case *ast.StructType:
return "struct type"
case *ast.SwitchStmt:
return "switch statement"
case *ast.TypeAssertExpr:
return "type assertion"
case *ast.TypeSpec:
return "type specification"
case *ast.TypeSwitchStmt:
return "type switch"
case *ast.UnaryExpr:
return fmt.Sprintf("unary %s operation", n.Op)
case *ast.ValueSpec:
return "value specification"
}
panic(fmt.Sprintf("unexpected node type: %T", n))
}
func is[T any](x any) bool {
_, ok := x.(T)
return ok
}

487
vendor/golang.org/x/tools/go/ast/astutil/imports.go generated vendored Normal file
View File

@@ -0,0 +1,487 @@
// Copyright 2013 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 astutil contains common utilities for working with the Go AST.
package astutil // import "golang.org/x/tools/go/ast/astutil"
import (
"fmt"
"go/ast"
"go/token"
"reflect"
"slices"
"strconv"
"strings"
)
// AddImport adds the import path to the file f, if absent.
func AddImport(fset *token.FileSet, f *ast.File, path string) (added bool) {
return AddNamedImport(fset, f, "", path)
}
// AddNamedImport adds the import with the given name and path to the file f, if absent.
// If name is not empty, it is used to rename the import.
//
// For example, calling
//
// AddNamedImport(fset, f, "pathpkg", "path")
//
// adds
//
// import pathpkg "path"
func AddNamedImport(fset *token.FileSet, f *ast.File, name, path string) (added bool) {
if imports(f, name, path) {
return false
}
newImport := &ast.ImportSpec{
Path: &ast.BasicLit{
Kind: token.STRING,
Value: strconv.Quote(path),
},
}
if name != "" {
newImport.Name = &ast.Ident{Name: name}
}
// Find an import decl to add to.
// The goal is to find an existing import
// whose import path has the longest shared
// prefix with path.
var (
bestMatch = -1 // length of longest shared prefix
lastImport = -1 // index in f.Decls of the file's final import decl
impDecl *ast.GenDecl // import decl containing the best match
impIndex = -1 // spec index in impDecl containing the best match
isThirdPartyPath = isThirdParty(path)
)
for i, decl := range f.Decls {
gen, ok := decl.(*ast.GenDecl)
if ok && gen.Tok == token.IMPORT {
lastImport = i
// Do not add to import "C", to avoid disrupting the
// association with its doc comment, breaking cgo.
if declImports(gen, "C") {
continue
}
// Match an empty import decl if that's all that is available.
if len(gen.Specs) == 0 && bestMatch == -1 {
impDecl = gen
}
// Compute longest shared prefix with imports in this group and find best
// matched import spec.
// 1. Always prefer import spec with longest shared prefix.
// 2. While match length is 0,
// - for stdlib package: prefer first import spec.
// - for third party package: prefer first third party import spec.
// We cannot use last import spec as best match for third party package
// because grouped imports are usually placed last by goimports -local
// flag.
// See issue #19190.
seenAnyThirdParty := false
for j, spec := range gen.Specs {
impspec := spec.(*ast.ImportSpec)
p := importPath(impspec)
n := matchLen(p, path)
if n > bestMatch || (bestMatch == 0 && !seenAnyThirdParty && isThirdPartyPath) {
bestMatch = n
impDecl = gen
impIndex = j
}
seenAnyThirdParty = seenAnyThirdParty || isThirdParty(p)
}
}
}
// If no import decl found, add one after the last import.
if impDecl == nil {
impDecl = &ast.GenDecl{
Tok: token.IMPORT,
}
if lastImport >= 0 {
impDecl.TokPos = f.Decls[lastImport].End()
} else {
// There are no existing imports.
// Our new import, preceded by a blank line, goes after the package declaration
// and after the comment, if any, that starts on the same line as the
// package declaration.
impDecl.TokPos = f.Package
file := fset.File(f.Package)
pkgLine := file.Line(f.Package)
for _, c := range f.Comments {
if file.Line(c.Pos()) > pkgLine {
break
}
// +2 for a blank line
impDecl.TokPos = c.End() + 2
}
}
f.Decls = append(f.Decls, nil)
copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:])
f.Decls[lastImport+1] = impDecl
}
// Insert new import at insertAt.
insertAt := 0
if impIndex >= 0 {
// insert after the found import
insertAt = impIndex + 1
}
impDecl.Specs = append(impDecl.Specs, nil)
copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:])
impDecl.Specs[insertAt] = newImport
pos := impDecl.Pos()
if insertAt > 0 {
// If there is a comment after an existing import, preserve the comment
// position by adding the new import after the comment.
if spec, ok := impDecl.Specs[insertAt-1].(*ast.ImportSpec); ok && spec.Comment != nil {
pos = spec.Comment.End()
} else {
// Assign same position as the previous import,
// so that the sorter sees it as being in the same block.
pos = impDecl.Specs[insertAt-1].Pos()
}
}
if newImport.Name != nil {
newImport.Name.NamePos = pos
}
updateBasicLitPos(newImport.Path, pos)
newImport.EndPos = pos
// Clean up parens. impDecl contains at least one spec.
if len(impDecl.Specs) == 1 {
// Remove unneeded parens.
impDecl.Lparen = token.NoPos
} else if !impDecl.Lparen.IsValid() {
// impDecl needs parens added.
impDecl.Lparen = impDecl.Specs[0].Pos()
}
f.Imports = append(f.Imports, newImport)
if len(f.Decls) <= 1 {
return true
}
// Merge all the import declarations into the first one.
var first *ast.GenDecl
for i := 0; i < len(f.Decls); i++ {
decl := f.Decls[i]
gen, ok := decl.(*ast.GenDecl)
if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") {
continue
}
if first == nil {
first = gen
continue // Don't touch the first one.
}
// We now know there is more than one package in this import
// declaration. Ensure that it ends up parenthesized.
first.Lparen = first.Pos()
// Move the imports of the other import declaration to the first one.
for _, spec := range gen.Specs {
updateBasicLitPos(spec.(*ast.ImportSpec).Path, first.Pos())
first.Specs = append(first.Specs, spec)
}
f.Decls = slices.Delete(f.Decls, i, i+1)
i--
}
return true
}
func isThirdParty(importPath string) bool {
// Third party package import path usually contains "." (".com", ".org", ...)
// This logic is taken from golang.org/x/tools/imports package.
return strings.Contains(importPath, ".")
}
// DeleteImport deletes the import path from the file f, if present.
// If there are duplicate import declarations, all matching ones are deleted.
func DeleteImport(fset *token.FileSet, f *ast.File, path string) (deleted bool) {
return DeleteNamedImport(fset, f, "", path)
}
// DeleteNamedImport deletes the import with the given name and path from the file f, if present.
// If there are duplicate import declarations, all matching ones are deleted.
func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (deleted bool) {
var (
delspecs = make(map[*ast.ImportSpec]bool)
delcomments = make(map[*ast.CommentGroup]bool)
)
// Find the import nodes that import path, if any.
for i := 0; i < len(f.Decls); i++ {
gen, ok := f.Decls[i].(*ast.GenDecl)
if !ok || gen.Tok != token.IMPORT {
continue
}
for j := 0; j < len(gen.Specs); j++ {
impspec := gen.Specs[j].(*ast.ImportSpec)
if importName(impspec) != name || importPath(impspec) != path {
continue
}
// We found an import spec that imports path.
// Delete it.
delspecs[impspec] = true
deleted = true
gen.Specs = slices.Delete(gen.Specs, j, j+1)
// If this was the last import spec in this decl,
// delete the decl, too.
if len(gen.Specs) == 0 {
f.Decls = slices.Delete(f.Decls, i, i+1)
i--
break
} else if len(gen.Specs) == 1 {
if impspec.Doc != nil {
delcomments[impspec.Doc] = true
}
if impspec.Comment != nil {
delcomments[impspec.Comment] = true
}
for _, cg := range f.Comments {
// Found comment on the same line as the import spec.
if cg.End() < impspec.Pos() && fset.Position(cg.End()).Line == fset.Position(impspec.Pos()).Line {
delcomments[cg] = true
break
}
}
spec := gen.Specs[0].(*ast.ImportSpec)
// Move the documentation right after the import decl.
if spec.Doc != nil {
for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Doc.Pos()).Line {
fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line)
}
}
for _, cg := range f.Comments {
if cg.End() < spec.Pos() && fset.Position(cg.End()).Line == fset.Position(spec.Pos()).Line {
for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Pos()).Line {
fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line)
}
break
}
}
}
if j > 0 {
lastImpspec := gen.Specs[j-1].(*ast.ImportSpec)
lastLine := fset.PositionFor(lastImpspec.Path.ValuePos, false).Line
line := fset.PositionFor(impspec.Path.ValuePos, false).Line
// We deleted an entry but now there may be
// a blank line-sized hole where the import was.
if line-lastLine > 1 || !gen.Rparen.IsValid() {
// There was a blank line immediately preceding the deleted import,
// so there's no need to close the hole. The right parenthesis is
// invalid after AddImport to an import statement without parenthesis.
// Do nothing.
} else if line != fset.File(gen.Rparen).LineCount() {
// There was no blank line. Close the hole.
fset.File(gen.Rparen).MergeLine(line)
}
}
j--
}
}
// Delete imports from f.Imports.
before := len(f.Imports)
f.Imports = slices.DeleteFunc(f.Imports, func(imp *ast.ImportSpec) bool {
_, ok := delspecs[imp]
return ok
})
if len(f.Imports)+len(delspecs) != before {
// This can happen when the AST is invalid (i.e. imports differ between f.Decls and f.Imports).
panic(fmt.Sprintf("deleted specs from Decls but not Imports: %v", delspecs))
}
// Delete comments from f.Comments.
f.Comments = slices.DeleteFunc(f.Comments, func(cg *ast.CommentGroup) bool {
_, ok := delcomments[cg]
return ok
})
return
}
// RewriteImport rewrites any import of path oldPath to path newPath.
func RewriteImport(fset *token.FileSet, f *ast.File, oldPath, newPath string) (rewrote bool) {
for _, imp := range f.Imports {
if importPath(imp) == oldPath {
rewrote = true
// record old End, because the default is to compute
// it using the length of imp.Path.Value.
imp.EndPos = imp.End()
imp.Path.Value = strconv.Quote(newPath)
}
}
return
}
// UsesImport reports whether a given import is used.
// The provided File must have been parsed with syntactic object resolution
// (not using go/parser.SkipObjectResolution).
func UsesImport(f *ast.File, path string) (used bool) {
if f.Scope == nil {
panic("file f was not parsed with syntactic object resolution")
}
spec := importSpec(f, path)
if spec == nil {
return
}
name := spec.Name.String()
switch name {
case "<nil>":
// If the package name is not explicitly specified,
// make an educated guess. This is not guaranteed to be correct.
lastSlash := strings.LastIndex(path, "/")
if lastSlash == -1 {
name = path
} else {
name = path[lastSlash+1:]
}
case "_", ".":
// Not sure if this import is used - err on the side of caution.
return true
}
ast.Walk(visitFn(func(n ast.Node) {
sel, ok := n.(*ast.SelectorExpr)
if ok && isTopName(sel.X, name) {
used = true
}
}), f)
return
}
type visitFn func(node ast.Node)
func (fn visitFn) Visit(node ast.Node) ast.Visitor {
fn(node)
return fn
}
// imports reports whether f has an import with the specified name and path.
func imports(f *ast.File, name, path string) bool {
for _, s := range f.Imports {
if importName(s) == name && importPath(s) == path {
return true
}
}
return false
}
// importSpec returns the import spec if f imports path,
// or nil otherwise.
func importSpec(f *ast.File, path string) *ast.ImportSpec {
for _, s := range f.Imports {
if importPath(s) == path {
return s
}
}
return nil
}
// importName returns the name of s,
// or "" if the import is not named.
func importName(s *ast.ImportSpec) string {
if s.Name == nil {
return ""
}
return s.Name.Name
}
// importPath returns the unquoted import path of s,
// or "" if the path is not properly quoted.
func importPath(s *ast.ImportSpec) string {
t, err := strconv.Unquote(s.Path.Value)
if err != nil {
return ""
}
return t
}
// declImports reports whether gen contains an import of path.
func declImports(gen *ast.GenDecl, path string) bool {
if gen.Tok != token.IMPORT {
return false
}
for _, spec := range gen.Specs {
impspec := spec.(*ast.ImportSpec)
if importPath(impspec) == path {
return true
}
}
return false
}
// matchLen returns the length of the longest path segment prefix shared by x and y.
func matchLen(x, y string) int {
n := 0
for i := 0; i < len(x) && i < len(y) && x[i] == y[i]; i++ {
if x[i] == '/' {
n++
}
}
return n
}
// isTopName returns true if n is a top-level unresolved identifier with the given name.
func isTopName(n ast.Expr, name string) bool {
id, ok := n.(*ast.Ident)
return ok && id.Name == name && id.Obj == nil
}
// Imports returns the file imports grouped by paragraph.
func Imports(fset *token.FileSet, f *ast.File) [][]*ast.ImportSpec {
var groups [][]*ast.ImportSpec
for _, decl := range f.Decls {
genDecl, ok := decl.(*ast.GenDecl)
if !ok || genDecl.Tok != token.IMPORT {
break
}
group := []*ast.ImportSpec{}
var lastLine int
for _, spec := range genDecl.Specs {
importSpec := spec.(*ast.ImportSpec)
pos := importSpec.Path.ValuePos
line := fset.Position(pos).Line
if lastLine > 0 && pos > 0 && line-lastLine > 1 {
groups = append(groups, group)
group = []*ast.ImportSpec{}
}
group = append(group, importSpec)
lastLine = line
}
groups = append(groups, group)
}
return groups
}
// updateBasicLitPos updates lit.Pos,
// ensuring that lit.End (if set) is displaced by the same amount.
// (See https://go.dev/issue/76395.)
func updateBasicLitPos(lit *ast.BasicLit, pos token.Pos) {
len := lit.End() - lit.Pos()
lit.ValuePos = pos
// TODO(adonovan): after go1.26, simplify to:
// lit.ValueEnd = pos + len
v := reflect.ValueOf(lit).Elem().FieldByName("ValueEnd")
if v.IsValid() && v.Int() != 0 {
v.SetInt(int64(pos + len))
}
}

490
vendor/golang.org/x/tools/go/ast/astutil/rewrite.go generated vendored Normal file
View File

@@ -0,0 +1,490 @@
// Copyright 2017 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 astutil
import (
"fmt"
"go/ast"
"reflect"
"sort"
)
// An ApplyFunc is invoked by Apply for each node n, even if n is nil,
// before and/or after the node's children, using a Cursor describing
// the current node and providing operations on it.
//
// The return value of ApplyFunc controls the syntax tree traversal.
// See Apply for details.
type ApplyFunc func(*Cursor) bool
// Apply traverses a syntax tree recursively, starting with root,
// and calling pre and post for each node as described below.
// Apply returns the syntax tree, possibly modified.
//
// If pre is not nil, it is called for each node before the node's
// children are traversed (pre-order). If pre returns false, no
// children are traversed, and post is not called for that node.
//
// If post is not nil, and a prior call of pre didn't return false,
// post is called for each node after its children are traversed
// (post-order). If post returns false, traversal is terminated and
// Apply returns immediately.
//
// Only fields that refer to AST nodes are considered children;
// i.e., token.Pos, Scopes, Objects, and fields of basic types
// (strings, etc.) are ignored.
//
// Children are traversed in the order in which they appear in the
// respective node's struct definition. A package's files are
// traversed in the filenames' alphabetical order.
func Apply(root ast.Node, pre, post ApplyFunc) (result ast.Node) {
parent := &struct{ ast.Node }{root}
defer func() {
if r := recover(); r != nil && r != abort {
panic(r)
}
result = parent.Node
}()
a := &application{pre: pre, post: post}
a.apply(parent, "Node", nil, root)
return
}
var abort = new(int) // singleton, to signal termination of Apply
// A Cursor describes a node encountered during Apply.
// Information about the node and its parent is available
// from the Node, Parent, Name, and Index methods.
//
// If p is a variable of type and value of the current parent node
// c.Parent(), and f is the field identifier with name c.Name(),
// the following invariants hold:
//
// p.f == c.Node() if c.Index() < 0
// p.f[c.Index()] == c.Node() if c.Index() >= 0
//
// The methods Replace, Delete, InsertBefore, and InsertAfter
// can be used to change the AST without disrupting Apply.
//
// This type is not to be confused with [inspector.Cursor] from
// package [golang.org/x/tools/go/ast/inspector], which provides
// stateless navigation of immutable syntax trees.
type Cursor struct {
parent ast.Node
name string
iter *iterator // valid if non-nil
node ast.Node
}
// Node returns the current Node.
func (c *Cursor) Node() ast.Node { return c.node }
// Parent returns the parent of the current Node.
func (c *Cursor) Parent() ast.Node { return c.parent }
// Name returns the name of the parent Node field that contains the current Node.
// If the parent is a *ast.Package and the current Node is a *ast.File, Name returns
// the filename for the current Node.
func (c *Cursor) Name() string { return c.name }
// Index reports the index >= 0 of the current Node in the slice of Nodes that
// contains it, or a value < 0 if the current Node is not part of a slice.
// The index of the current node changes if InsertBefore is called while
// processing the current node.
func (c *Cursor) Index() int {
if c.iter != nil {
return c.iter.index
}
return -1
}
// field returns the current node's parent field value.
func (c *Cursor) field() reflect.Value {
return reflect.Indirect(reflect.ValueOf(c.parent)).FieldByName(c.name)
}
// Replace replaces the current Node with n.
// The replacement node is not walked by Apply.
func (c *Cursor) Replace(n ast.Node) {
if _, ok := c.node.(*ast.File); ok {
file, ok := n.(*ast.File)
if !ok {
panic("attempt to replace *ast.File with non-*ast.File")
}
c.parent.(*ast.Package).Files[c.name] = file
return
}
v := c.field()
if i := c.Index(); i >= 0 {
v = v.Index(i)
}
v.Set(reflect.ValueOf(n))
}
// Delete deletes the current Node from its containing slice.
// If the current Node is not part of a slice, Delete panics.
// As a special case, if the current node is a package file,
// Delete removes it from the package's Files map.
func (c *Cursor) Delete() {
if _, ok := c.node.(*ast.File); ok {
delete(c.parent.(*ast.Package).Files, c.name)
return
}
i := c.Index()
if i < 0 {
panic("Delete node not contained in slice")
}
v := c.field()
l := v.Len()
reflect.Copy(v.Slice(i, l), v.Slice(i+1, l))
v.Index(l - 1).Set(reflect.Zero(v.Type().Elem()))
v.SetLen(l - 1)
c.iter.step--
}
// InsertAfter inserts n after the current Node in its containing slice.
// If the current Node is not part of a slice, InsertAfter panics.
// Apply does not walk n.
func (c *Cursor) InsertAfter(n ast.Node) {
i := c.Index()
if i < 0 {
panic("InsertAfter node not contained in slice")
}
v := c.field()
v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem())))
l := v.Len()
reflect.Copy(v.Slice(i+2, l), v.Slice(i+1, l))
v.Index(i + 1).Set(reflect.ValueOf(n))
c.iter.step++
}
// InsertBefore inserts n before the current Node in its containing slice.
// If the current Node is not part of a slice, InsertBefore panics.
// Apply will not walk n.
func (c *Cursor) InsertBefore(n ast.Node) {
i := c.Index()
if i < 0 {
panic("InsertBefore node not contained in slice")
}
v := c.field()
v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem())))
l := v.Len()
reflect.Copy(v.Slice(i+1, l), v.Slice(i, l))
v.Index(i).Set(reflect.ValueOf(n))
c.iter.index++
}
// application carries all the shared data so we can pass it around cheaply.
type application struct {
pre, post ApplyFunc
cursor Cursor
iter iterator
}
func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast.Node) {
// convert typed nil into untyped nil
if v := reflect.ValueOf(n); v.Kind() == reflect.Pointer && v.IsNil() {
n = nil
}
// avoid heap-allocating a new cursor for each apply call; reuse a.cursor instead
saved := a.cursor
a.cursor.parent = parent
a.cursor.name = name
a.cursor.iter = iter
a.cursor.node = n
if a.pre != nil && !a.pre(&a.cursor) {
a.cursor = saved
return
}
// walk children
// (the order of the cases matches the order of the corresponding node types in go/ast)
switch n := n.(type) {
case nil:
// nothing to do
// Comments and fields
case *ast.Comment:
// nothing to do
case *ast.CommentGroup:
if n != nil {
a.applyList(n, "List")
}
case *ast.Field:
a.apply(n, "Doc", nil, n.Doc)
a.applyList(n, "Names")
a.apply(n, "Type", nil, n.Type)
a.apply(n, "Tag", nil, n.Tag)
a.apply(n, "Comment", nil, n.Comment)
case *ast.FieldList:
a.applyList(n, "List")
// Expressions
case *ast.BadExpr, *ast.Ident, *ast.BasicLit:
// nothing to do
case *ast.Ellipsis:
a.apply(n, "Elt", nil, n.Elt)
case *ast.FuncLit:
a.apply(n, "Type", nil, n.Type)
a.apply(n, "Body", nil, n.Body)
case *ast.CompositeLit:
a.apply(n, "Type", nil, n.Type)
a.applyList(n, "Elts")
case *ast.ParenExpr:
a.apply(n, "X", nil, n.X)
case *ast.SelectorExpr:
a.apply(n, "X", nil, n.X)
a.apply(n, "Sel", nil, n.Sel)
case *ast.IndexExpr:
a.apply(n, "X", nil, n.X)
a.apply(n, "Index", nil, n.Index)
case *ast.IndexListExpr:
a.apply(n, "X", nil, n.X)
a.applyList(n, "Indices")
case *ast.SliceExpr:
a.apply(n, "X", nil, n.X)
a.apply(n, "Low", nil, n.Low)
a.apply(n, "High", nil, n.High)
a.apply(n, "Max", nil, n.Max)
case *ast.TypeAssertExpr:
a.apply(n, "X", nil, n.X)
a.apply(n, "Type", nil, n.Type)
case *ast.CallExpr:
a.apply(n, "Fun", nil, n.Fun)
a.applyList(n, "Args")
case *ast.StarExpr:
a.apply(n, "X", nil, n.X)
case *ast.UnaryExpr:
a.apply(n, "X", nil, n.X)
case *ast.BinaryExpr:
a.apply(n, "X", nil, n.X)
a.apply(n, "Y", nil, n.Y)
case *ast.KeyValueExpr:
a.apply(n, "Key", nil, n.Key)
a.apply(n, "Value", nil, n.Value)
// Types
case *ast.ArrayType:
a.apply(n, "Len", nil, n.Len)
a.apply(n, "Elt", nil, n.Elt)
case *ast.StructType:
a.apply(n, "Fields", nil, n.Fields)
case *ast.FuncType:
if tparams := n.TypeParams; tparams != nil {
a.apply(n, "TypeParams", nil, tparams)
}
a.apply(n, "Params", nil, n.Params)
a.apply(n, "Results", nil, n.Results)
case *ast.InterfaceType:
a.apply(n, "Methods", nil, n.Methods)
case *ast.MapType:
a.apply(n, "Key", nil, n.Key)
a.apply(n, "Value", nil, n.Value)
case *ast.ChanType:
a.apply(n, "Value", nil, n.Value)
// Statements
case *ast.BadStmt:
// nothing to do
case *ast.DeclStmt:
a.apply(n, "Decl", nil, n.Decl)
case *ast.EmptyStmt:
// nothing to do
case *ast.LabeledStmt:
a.apply(n, "Label", nil, n.Label)
a.apply(n, "Stmt", nil, n.Stmt)
case *ast.ExprStmt:
a.apply(n, "X", nil, n.X)
case *ast.SendStmt:
a.apply(n, "Chan", nil, n.Chan)
a.apply(n, "Value", nil, n.Value)
case *ast.IncDecStmt:
a.apply(n, "X", nil, n.X)
case *ast.AssignStmt:
a.applyList(n, "Lhs")
a.applyList(n, "Rhs")
case *ast.GoStmt:
a.apply(n, "Call", nil, n.Call)
case *ast.DeferStmt:
a.apply(n, "Call", nil, n.Call)
case *ast.ReturnStmt:
a.applyList(n, "Results")
case *ast.BranchStmt:
a.apply(n, "Label", nil, n.Label)
case *ast.BlockStmt:
a.applyList(n, "List")
case *ast.IfStmt:
a.apply(n, "Init", nil, n.Init)
a.apply(n, "Cond", nil, n.Cond)
a.apply(n, "Body", nil, n.Body)
a.apply(n, "Else", nil, n.Else)
case *ast.CaseClause:
a.applyList(n, "List")
a.applyList(n, "Body")
case *ast.SwitchStmt:
a.apply(n, "Init", nil, n.Init)
a.apply(n, "Tag", nil, n.Tag)
a.apply(n, "Body", nil, n.Body)
case *ast.TypeSwitchStmt:
a.apply(n, "Init", nil, n.Init)
a.apply(n, "Assign", nil, n.Assign)
a.apply(n, "Body", nil, n.Body)
case *ast.CommClause:
a.apply(n, "Comm", nil, n.Comm)
a.applyList(n, "Body")
case *ast.SelectStmt:
a.apply(n, "Body", nil, n.Body)
case *ast.ForStmt:
a.apply(n, "Init", nil, n.Init)
a.apply(n, "Cond", nil, n.Cond)
a.apply(n, "Post", nil, n.Post)
a.apply(n, "Body", nil, n.Body)
case *ast.RangeStmt:
a.apply(n, "Key", nil, n.Key)
a.apply(n, "Value", nil, n.Value)
a.apply(n, "X", nil, n.X)
a.apply(n, "Body", nil, n.Body)
// Declarations
case *ast.ImportSpec:
a.apply(n, "Doc", nil, n.Doc)
a.apply(n, "Name", nil, n.Name)
a.apply(n, "Path", nil, n.Path)
a.apply(n, "Comment", nil, n.Comment)
case *ast.ValueSpec:
a.apply(n, "Doc", nil, n.Doc)
a.applyList(n, "Names")
a.apply(n, "Type", nil, n.Type)
a.applyList(n, "Values")
a.apply(n, "Comment", nil, n.Comment)
case *ast.TypeSpec:
a.apply(n, "Doc", nil, n.Doc)
a.apply(n, "Name", nil, n.Name)
if tparams := n.TypeParams; tparams != nil {
a.apply(n, "TypeParams", nil, tparams)
}
a.apply(n, "Type", nil, n.Type)
a.apply(n, "Comment", nil, n.Comment)
case *ast.BadDecl:
// nothing to do
case *ast.GenDecl:
a.apply(n, "Doc", nil, n.Doc)
a.applyList(n, "Specs")
case *ast.FuncDecl:
a.apply(n, "Doc", nil, n.Doc)
a.apply(n, "Recv", nil, n.Recv)
a.apply(n, "Name", nil, n.Name)
a.apply(n, "Type", nil, n.Type)
a.apply(n, "Body", nil, n.Body)
// Files and packages
case *ast.File:
a.apply(n, "Doc", nil, n.Doc)
a.apply(n, "Name", nil, n.Name)
a.applyList(n, "Decls")
// Don't walk n.Comments; they have either been walked already if
// they are Doc comments, or they can be easily walked explicitly.
case *ast.Package:
// collect and sort names for reproducible behavior
var names []string
for name := range n.Files {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
a.apply(n, name, nil, n.Files[name])
}
default:
panic(fmt.Sprintf("Apply: unexpected node type %T", n))
}
if a.post != nil && !a.post(&a.cursor) {
panic(abort)
}
a.cursor = saved
}
// An iterator controls iteration over a slice of nodes.
type iterator struct {
index, step int
}
func (a *application) applyList(parent ast.Node, name string) {
// avoid heap-allocating a new iterator for each applyList call; reuse a.iter instead
saved := a.iter
a.iter.index = 0
for {
// must reload parent.name each time, since cursor modifications might change it
v := reflect.Indirect(reflect.ValueOf(parent)).FieldByName(name)
if a.iter.index >= v.Len() {
break
}
// element x may be nil in a bad AST - be cautious
var x ast.Node
if e := v.Index(a.iter.index); e.IsValid() {
x = e.Interface().(ast.Node)
}
a.iter.step = 1
a.apply(parent, name, &a.iter, x)
a.iter.index += a.iter.step
}
a.iter = saved
}

13
vendor/golang.org/x/tools/go/ast/astutil/util.go generated vendored Normal file
View File

@@ -0,0 +1,13 @@
// 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.
package astutil
import "go/ast"
// Unparen returns e with any enclosing parentheses stripped.
// Deprecated: use [ast.Unparen].
//
//go:fix inline
func Unparen(e ast.Expr) ast.Expr { return ast.Unparen(e) }

295
vendor/golang.org/x/tools/go/ast/edge/edge.go generated vendored Normal file
View File

@@ -0,0 +1,295 @@
// 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 edge defines identifiers for each field of an ast.Node
// struct type that refers to another Node.
package edge
import (
"fmt"
"go/ast"
"reflect"
)
// A Kind describes a field of an ast.Node struct.
type Kind uint8
// String returns a description of the edge kind.
func (k Kind) String() string {
if k == Invalid {
return "<invalid>"
}
info := fieldInfos[k]
return fmt.Sprintf("%v.%s", info.nodeType.Elem().Name(), info.name)
}
// NodeType returns the pointer-to-struct type of the ast.Node implementation.
func (k Kind) NodeType() reflect.Type { return fieldInfos[k].nodeType }
// FieldName returns the name of the field.
func (k Kind) FieldName() string { return fieldInfos[k].name }
// FieldType returns the declared type of the field.
func (k Kind) FieldType() reflect.Type { return fieldInfos[k].fieldType }
// Get returns the direct child of n identified by (k, idx).
// n's type must match k.NodeType().
// idx must be a valid slice index, or -1 for a non-slice.
func (k Kind) Get(n ast.Node, idx int) ast.Node {
if k.NodeType() != reflect.TypeOf(n) {
panic(fmt.Sprintf("%v.Get(%T): invalid node type", k, n))
}
v := reflect.ValueOf(n).Elem().Field(fieldInfos[k].index)
if idx != -1 {
v = v.Index(idx) // asserts valid index
} else {
// (The type assertion below asserts that v is not a slice.)
}
return v.Interface().(ast.Node) // may be nil
}
const (
Invalid Kind = iota // for nodes at the root of the traversal
// Kinds are sorted alphabetically.
// Numbering is not stable.
// Each is named Type_Field, where Type is the
// ast.Node struct type and Field is the name of the field
ArrayType_Elt
ArrayType_Len
AssignStmt_Lhs
AssignStmt_Rhs
BinaryExpr_X
BinaryExpr_Y
BlockStmt_List
BranchStmt_Label
CallExpr_Args
CallExpr_Fun
CaseClause_Body
CaseClause_List
ChanType_Value
CommClause_Body
CommClause_Comm
CommentGroup_List
CompositeLit_Elts
CompositeLit_Type
DeclStmt_Decl
DeferStmt_Call
Ellipsis_Elt
ExprStmt_X
FieldList_List
Field_Comment
Field_Doc
Field_Names
Field_Tag
Field_Type
File_Decls
File_Doc
File_Name
ForStmt_Body
ForStmt_Cond
ForStmt_Init
ForStmt_Post
FuncDecl_Body
FuncDecl_Doc
FuncDecl_Name
FuncDecl_Recv
FuncDecl_Type
FuncLit_Body
FuncLit_Type
FuncType_Params
FuncType_Results
FuncType_TypeParams
GenDecl_Doc
GenDecl_Specs
GoStmt_Call
IfStmt_Body
IfStmt_Cond
IfStmt_Else
IfStmt_Init
ImportSpec_Comment
ImportSpec_Doc
ImportSpec_Name
ImportSpec_Path
IncDecStmt_X
IndexExpr_Index
IndexExpr_X
IndexListExpr_Indices
IndexListExpr_X
InterfaceType_Methods
KeyValueExpr_Key
KeyValueExpr_Value
LabeledStmt_Label
LabeledStmt_Stmt
MapType_Key
MapType_Value
ParenExpr_X
RangeStmt_Body
RangeStmt_Key
RangeStmt_Value
RangeStmt_X
ReturnStmt_Results
SelectStmt_Body
SelectorExpr_Sel
SelectorExpr_X
SendStmt_Chan
SendStmt_Value
SliceExpr_High
SliceExpr_Low
SliceExpr_Max
SliceExpr_X
StarExpr_X
StructType_Fields
SwitchStmt_Body
SwitchStmt_Init
SwitchStmt_Tag
TypeAssertExpr_Type
TypeAssertExpr_X
TypeSpec_Comment
TypeSpec_Doc
TypeSpec_Name
TypeSpec_Type
TypeSpec_TypeParams
TypeSwitchStmt_Assign
TypeSwitchStmt_Body
TypeSwitchStmt_Init
UnaryExpr_X
ValueSpec_Comment
ValueSpec_Doc
ValueSpec_Names
ValueSpec_Type
ValueSpec_Values
maxKind
)
// Assert that the encoding fits in 7 bits,
// as the inspector relies on this.
// (We are currently at 104.)
var _ = [1 << 7]struct{}{}[maxKind]
type fieldInfo struct {
nodeType reflect.Type // pointer-to-struct type of ast.Node implementation
name string
index int
fieldType reflect.Type
}
func info[N ast.Node](fieldName string) fieldInfo {
nodePtrType := reflect.TypeFor[N]()
f, ok := nodePtrType.Elem().FieldByName(fieldName)
if !ok {
panic(fieldName)
}
return fieldInfo{nodePtrType, fieldName, f.Index[0], f.Type}
}
var fieldInfos = [...]fieldInfo{
Invalid: {},
ArrayType_Elt: info[*ast.ArrayType]("Elt"),
ArrayType_Len: info[*ast.ArrayType]("Len"),
AssignStmt_Lhs: info[*ast.AssignStmt]("Lhs"),
AssignStmt_Rhs: info[*ast.AssignStmt]("Rhs"),
BinaryExpr_X: info[*ast.BinaryExpr]("X"),
BinaryExpr_Y: info[*ast.BinaryExpr]("Y"),
BlockStmt_List: info[*ast.BlockStmt]("List"),
BranchStmt_Label: info[*ast.BranchStmt]("Label"),
CallExpr_Args: info[*ast.CallExpr]("Args"),
CallExpr_Fun: info[*ast.CallExpr]("Fun"),
CaseClause_Body: info[*ast.CaseClause]("Body"),
CaseClause_List: info[*ast.CaseClause]("List"),
ChanType_Value: info[*ast.ChanType]("Value"),
CommClause_Body: info[*ast.CommClause]("Body"),
CommClause_Comm: info[*ast.CommClause]("Comm"),
CommentGroup_List: info[*ast.CommentGroup]("List"),
CompositeLit_Elts: info[*ast.CompositeLit]("Elts"),
CompositeLit_Type: info[*ast.CompositeLit]("Type"),
DeclStmt_Decl: info[*ast.DeclStmt]("Decl"),
DeferStmt_Call: info[*ast.DeferStmt]("Call"),
Ellipsis_Elt: info[*ast.Ellipsis]("Elt"),
ExprStmt_X: info[*ast.ExprStmt]("X"),
FieldList_List: info[*ast.FieldList]("List"),
Field_Comment: info[*ast.Field]("Comment"),
Field_Doc: info[*ast.Field]("Doc"),
Field_Names: info[*ast.Field]("Names"),
Field_Tag: info[*ast.Field]("Tag"),
Field_Type: info[*ast.Field]("Type"),
File_Decls: info[*ast.File]("Decls"),
File_Doc: info[*ast.File]("Doc"),
File_Name: info[*ast.File]("Name"),
ForStmt_Body: info[*ast.ForStmt]("Body"),
ForStmt_Cond: info[*ast.ForStmt]("Cond"),
ForStmt_Init: info[*ast.ForStmt]("Init"),
ForStmt_Post: info[*ast.ForStmt]("Post"),
FuncDecl_Body: info[*ast.FuncDecl]("Body"),
FuncDecl_Doc: info[*ast.FuncDecl]("Doc"),
FuncDecl_Name: info[*ast.FuncDecl]("Name"),
FuncDecl_Recv: info[*ast.FuncDecl]("Recv"),
FuncDecl_Type: info[*ast.FuncDecl]("Type"),
FuncLit_Body: info[*ast.FuncLit]("Body"),
FuncLit_Type: info[*ast.FuncLit]("Type"),
FuncType_Params: info[*ast.FuncType]("Params"),
FuncType_Results: info[*ast.FuncType]("Results"),
FuncType_TypeParams: info[*ast.FuncType]("TypeParams"),
GenDecl_Doc: info[*ast.GenDecl]("Doc"),
GenDecl_Specs: info[*ast.GenDecl]("Specs"),
GoStmt_Call: info[*ast.GoStmt]("Call"),
IfStmt_Body: info[*ast.IfStmt]("Body"),
IfStmt_Cond: info[*ast.IfStmt]("Cond"),
IfStmt_Else: info[*ast.IfStmt]("Else"),
IfStmt_Init: info[*ast.IfStmt]("Init"),
ImportSpec_Comment: info[*ast.ImportSpec]("Comment"),
ImportSpec_Doc: info[*ast.ImportSpec]("Doc"),
ImportSpec_Name: info[*ast.ImportSpec]("Name"),
ImportSpec_Path: info[*ast.ImportSpec]("Path"),
IncDecStmt_X: info[*ast.IncDecStmt]("X"),
IndexExpr_Index: info[*ast.IndexExpr]("Index"),
IndexExpr_X: info[*ast.IndexExpr]("X"),
IndexListExpr_Indices: info[*ast.IndexListExpr]("Indices"),
IndexListExpr_X: info[*ast.IndexListExpr]("X"),
InterfaceType_Methods: info[*ast.InterfaceType]("Methods"),
KeyValueExpr_Key: info[*ast.KeyValueExpr]("Key"),
KeyValueExpr_Value: info[*ast.KeyValueExpr]("Value"),
LabeledStmt_Label: info[*ast.LabeledStmt]("Label"),
LabeledStmt_Stmt: info[*ast.LabeledStmt]("Stmt"),
MapType_Key: info[*ast.MapType]("Key"),
MapType_Value: info[*ast.MapType]("Value"),
ParenExpr_X: info[*ast.ParenExpr]("X"),
RangeStmt_Body: info[*ast.RangeStmt]("Body"),
RangeStmt_Key: info[*ast.RangeStmt]("Key"),
RangeStmt_Value: info[*ast.RangeStmt]("Value"),
RangeStmt_X: info[*ast.RangeStmt]("X"),
ReturnStmt_Results: info[*ast.ReturnStmt]("Results"),
SelectStmt_Body: info[*ast.SelectStmt]("Body"),
SelectorExpr_Sel: info[*ast.SelectorExpr]("Sel"),
SelectorExpr_X: info[*ast.SelectorExpr]("X"),
SendStmt_Chan: info[*ast.SendStmt]("Chan"),
SendStmt_Value: info[*ast.SendStmt]("Value"),
SliceExpr_High: info[*ast.SliceExpr]("High"),
SliceExpr_Low: info[*ast.SliceExpr]("Low"),
SliceExpr_Max: info[*ast.SliceExpr]("Max"),
SliceExpr_X: info[*ast.SliceExpr]("X"),
StarExpr_X: info[*ast.StarExpr]("X"),
StructType_Fields: info[*ast.StructType]("Fields"),
SwitchStmt_Body: info[*ast.SwitchStmt]("Body"),
SwitchStmt_Init: info[*ast.SwitchStmt]("Init"),
SwitchStmt_Tag: info[*ast.SwitchStmt]("Tag"),
TypeAssertExpr_Type: info[*ast.TypeAssertExpr]("Type"),
TypeAssertExpr_X: info[*ast.TypeAssertExpr]("X"),
TypeSpec_Comment: info[*ast.TypeSpec]("Comment"),
TypeSpec_Doc: info[*ast.TypeSpec]("Doc"),
TypeSpec_Name: info[*ast.TypeSpec]("Name"),
TypeSpec_Type: info[*ast.TypeSpec]("Type"),
TypeSpec_TypeParams: info[*ast.TypeSpec]("TypeParams"),
TypeSwitchStmt_Assign: info[*ast.TypeSwitchStmt]("Assign"),
TypeSwitchStmt_Body: info[*ast.TypeSwitchStmt]("Body"),
TypeSwitchStmt_Init: info[*ast.TypeSwitchStmt]("Init"),
UnaryExpr_X: info[*ast.UnaryExpr]("X"),
ValueSpec_Comment: info[*ast.ValueSpec]("Comment"),
ValueSpec_Doc: info[*ast.ValueSpec]("Doc"),
ValueSpec_Names: info[*ast.ValueSpec]("Names"),
ValueSpec_Type: info[*ast.ValueSpec]("Type"),
ValueSpec_Values: info[*ast.ValueSpec]("Values"),
}

527
vendor/golang.org/x/tools/go/ast/inspector/cursor.go generated vendored Normal file
View File

@@ -0,0 +1,527 @@
// 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 inspector
import (
"fmt"
"go/ast"
"go/token"
"iter"
"reflect"
"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 [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
index int32 // index of push node; -1 for virtual root node
}
// Root returns a cursor for the virtual root node,
// whose children are the files provided to [New].
//
// Its [Cursor.Node] method return nil.
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 (in *Inspector) At(index int32) Cursor {
if index < 0 {
panic("negative index")
}
if int(index) >= len(in.events) {
panic("index out of range for this inspector")
}
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 { return c.in }
// Index returns the index of this cursor position within the package.
//
// Clients should not assume anything about the numeric Index value
// except that it increases monotonically throughout the traversal.
// It is provided for use with [At].
//
// Index must not be called on the Root node.
func (c Cursor) Index() int32 {
if c.index < 0 {
panic("Index called on Root node")
}
return c.index
}
// Node returns the node at the current cursor position,
// or nil for the cursor returned by [Inspector.Root].
func (c Cursor) Node() ast.Node {
if c.index < 0 {
return nil
}
return c.in.events[c.index].node
}
// String returns information about the cursor's node, if any.
func (c Cursor) String() string {
if c.in == nil {
return "(invalid)"
}
if c.index < 0 {
return "(root)"
}
return reflect.TypeOf(c.Node()).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.in.events)) // root: all events
} else {
return c.index, c.in.events[c.index].index + 1 // just one subtree
}
}
// Preorder returns an iterator over the nodes of the subtree
// represented by c in depth-first order. Each node in the sequence is
// represented by a Cursor that allows access to the Node, but may
// also be used to start a new traversal, or to obtain the stack of
// nodes enclosing the cursor.
//
// The 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.
//
// If you need control over descent into subtrees,
// or need both pre- and post-order notifications, use [Cursor.Inspect]
func (c Cursor) Preorder(types ...ast.Node) iter.Seq[Cursor] {
mask := maskOf(types)
return func(yield func(Cursor) bool) {
events := c.in.events
for i, limit := c.indices(); i < limit; {
ev := events[i]
if ev.index > i { // push?
if ev.typ&mask != 0 && !yield(Cursor{c.in, i}) {
break
}
pop := ev.index
if events[pop].typ&mask == 0 {
// Subtree does not contain types: skip.
i = pop + 1
continue
}
}
i++
}
}
}
// Inspect visits the nodes of the subtree represented by c in
// depth-first order. It calls f(n) for each node n before it
// visits n's children. If f returns true, Inspect invokes f
// recursively for each of the non-nil children of the node.
//
// Each node is represented by a Cursor that allows access to the
// Node, but may also be used to start a new traversal, or to obtain
// the stack of nodes enclosing the cursor.
//
// 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.
func (c Cursor) Inspect(types []ast.Node, f func(c Cursor) (descend bool)) {
mask := maskOf(types)
events := c.in.events
for i, limit := c.indices(); i < limit; {
ev := events[i]
if ev.index > i {
// push
pop := ev.index
if ev.typ&mask != 0 && !f(Cursor{c.in, i}) ||
events[pop].typ&mask == 0 {
// The user opted not to descend, or the
// subtree does not contain types:
// skip past the pop.
i = pop + 1
continue
}
}
i++
}
}
// Enclosing returns an iterator over the nodes enclosing the current
// current node, starting with the Cursor itself.
//
// Enclosing must not be called on the Root node (whose [Cursor.Node] returns nil).
//
// The types argument, if non-empty, enables type-based filtering of
// events: the sequence includes only enclosing nodes whose type
// matches an element of the types slice.
func (c Cursor) Enclosing(types ...ast.Node) iter.Seq[Cursor] {
if c.index < 0 {
panic("Cursor.Enclosing called on Root node")
}
mask := maskOf(types)
return func(yield func(Cursor) bool) {
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
}
}
}
}
// Parent returns the parent of the current node.
//
// Parent must not be called on the Root node (whose [Cursor.Node] returns nil).
func (c Cursor) Parent() Cursor {
if c.index < 0 {
panic("Cursor.Parent called on Root node")
}
return Cursor{c.in, c.in.events[c.index].parent}
}
// ParentEdge returns the identity of the field in the parent node
// that holds this cursor's node, and if it is a list, the index within it.
//
// For example, f(x, y) is a CallExpr whose three children are Idents.
// f has edge kind [edge.CallExpr_Fun] and index -1.
// x and y have kind [edge.CallExpr_Args] and indices 0 and 1, respectively.
//
// If called on a child of the Root node, it returns ([edge.Invalid], -1).
//
// ParentEdge must not be called on the Root node (whose [Cursor.Node] returns nil).
func (c Cursor) ParentEdge() (edge.Kind, int) {
if c.index < 0 {
panic("Cursor.ParentEdge called on Root node")
}
events := c.in.events
pop := events[c.index].index
return unpackEdgeKindAndIndex(events[pop].parent)
}
// ChildAt returns the cursor for the child of the
// current node identified by its edge and index.
// The index must be -1 if the edge.Kind is not a slice.
// The indicated child node must exist.
//
// ChildAt must not be called on the Root node (whose [Cursor.Node] returns nil).
//
// Invariant: c.Parent().ChildAt(c.ParentEdge()) == c.
func (c Cursor) ChildAt(k edge.Kind, idx int) Cursor {
target := packEdgeKindAndIndex(k, idx)
// Unfortunately there's no shortcut to looping.
events := c.in.events
i := c.index + 1
for {
pop := events[i].index
if pop < i {
break
}
if events[pop].parent == target {
return Cursor{c.in, i}
}
i = pop + 1
}
panic(fmt.Sprintf("ChildAt(%v, %d): no such child of %v", k, idx, c))
}
// Child returns the cursor for n, which must be a direct child of c's Node.
//
// Child must not be called on the Root node (whose [Cursor.Node] returns nil).
func (c Cursor) Child(n ast.Node) Cursor {
if c.index < 0 {
panic("Cursor.Child called on Root node")
}
if false {
// reference implementation
for child := range c.Children() {
if child.Node() == n {
return child
}
}
} else {
// optimized implementation
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}
}
}
}
panic(fmt.Sprintf("Child(%T): not a child of %v", n, c))
}
// NextSibling returns the cursor for the next sibling node in the same list
// (for example, of files, decls, specs, statements, fields, or expressions) as
// the current node. It returns (zero, false) if the node is the last node in
// the list, or is not part of a list.
//
// NextSibling must not be called on the Root node.
//
// See note at [Cursor.Children].
func (c Cursor) NextSibling() (Cursor, bool) {
if c.index < 0 {
panic("Cursor.NextSibling called on Root node")
}
events := c.in.events
i := events[c.index].index + 1 // after corresponding pop
if i < int32(len(events)) {
if events[i].index > i { // push?
return Cursor{c.in, i}, true
}
}
return Cursor{}, false
}
// PrevSibling returns the cursor for the previous sibling node in the
// same list (for example, of files, decls, specs, statements, fields,
// or expressions) as the current node. It returns zero if the node is
// the first node in the list, or is not part of a list.
//
// It must not be called on the Root node.
//
// See note at [Cursor.Children].
func (c Cursor) PrevSibling() (Cursor, bool) {
if c.index < 0 {
panic("Cursor.PrevSibling called on Root node")
}
events := c.in.events
i := c.index - 1
if i >= 0 {
if j := events[i].index; j < i { // pop?
return Cursor{c.in, j}, true
}
}
return Cursor{}, false
}
// 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.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
}
return Cursor{}, false
}
// 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.in.events
if c.index < 0 { // root?
if len(events) > 0 {
// return push of final event (a pop)
return Cursor{c.in, events[len(events)-1].index}, true
}
} else {
j := events[c.index].index - 1 // before corresponding pop
// Inv: j == c.index if c has no children
// or j is last child's pop.
if j > c.index { // c has children
return Cursor{c.in, events[j].index}, true
}
}
return Cursor{}, false
}
// Children returns an iterator over the direct children of the
// current node, if any.
//
// When using Children, NextChild, and PrevChild, bear in mind that a
// Node's children may come from different fields, some of which may
// be lists of nodes without a distinguished intervening container
// such as [ast.BlockStmt].
//
// For example, [ast.CaseClause] has a field List of expressions and a
// field Body of statements, so the children of a CaseClause are a mix
// of expressions and statements. Other nodes that have "uncontained"
// list fields include:
//
// - [ast.ValueSpec] (Names, Values)
// - [ast.CompositeLit] (Type, Elts)
// - [ast.IndexListExpr] (X, Indices)
// - [ast.CallExpr] (Fun, Args)
// - [ast.AssignStmt] (Lhs, Rhs)
//
// So, do not assume that the previous sibling of an ast.Stmt is also
// an ast.Stmt, or if it is, that they are executed sequentially,
// unless you have established that, say, its parent is a BlockStmt
// or its [Cursor.ParentEdge] is [edge.BlockStmt_List].
// For example, given "for S1; ; S2 {}", the predecessor of S2 is S1,
// even though they are not executed in sequence.
func (c Cursor) Children() iter.Seq[Cursor] {
return func(yield func(Cursor) bool) {
c, ok := c.FirstChild()
for ok && yield(c) {
c, ok = c.NextSibling()
}
}
}
// Contains reports whether c contains or is equal to c2.
//
// Both Cursors must belong to the same [Inspector];
// neither may be its Root node.
func (c Cursor) Contains(c2 Cursor) bool {
if c.in != c2.in {
panic("different inspectors")
}
events := c.in.events
return c.index <= c2.index && events[c2.index].index <= events[c.index].index
}
// FindNode returns the cursor for node n if it belongs to the subtree
// rooted at c. It returns zero if n is not found.
func (c Cursor) FindNode(n ast.Node) (Cursor, bool) {
// FindNode is equivalent to this code,
// but more convenient and 15-20% faster:
if false {
for candidate := range c.Preorder(n) {
if candidate.Node() == n {
return candidate, true
}
}
return Cursor{}, false
}
// TODO(adonovan): opt: should we assume Node.Pos is accurate
// and combine type-based filtering with position filtering
// like FindByPos?
mask := maskOf([]ast.Node{n})
events := c.in.events
for i, limit := c.indices(); i < limit; i++ {
ev := events[i]
if ev.index > i { // push?
if ev.typ&mask != 0 && ev.node == n {
return Cursor{c.in, i}, true
}
pop := ev.index
if events[pop].typ&mask == 0 {
// Subtree does not contain type of n: skip.
i = pop
}
}
}
return Cursor{}, false
}
// FindByPos returns the cursor for the innermost node n in the tree
// rooted at c such that n.Pos() <= start && end <= n.End().
// (For an *ast.File, it uses the bounds n.FileStart-n.FileEnd.)
//
// An empty range (start == end) between two adjacent nodes is
// considered to belong to the first node.
//
// It returns zero if none is found.
// Precondition: start <= end.
//
// See also [astutil.PathEnclosingInterval], which
// tolerates adjoining whitespace.
func (c Cursor) FindByPos(start, end token.Pos) (Cursor, bool) {
if end < start {
panic("end < start")
}
events := c.in.events
// This algorithm could be implemented using c.Inspect,
// but it is about 2.5x slower.
// best is the push-index of the latest (=innermost) node containing range.
// (Beware: latest is not always innermost because FuncDecl.{Name,Type} overlap.)
best := int32(-1)
for i, limit := c.indices(); i < limit; i++ {
ev := events[i]
if ev.index > i { // push?
n := ev.node
var nodeEnd token.Pos
if file, ok := n.(*ast.File); ok {
nodeEnd = file.FileEnd
// Note: files may be out of Pos order.
if file.FileStart > start {
i = ev.index // disjoint, after; skip to next file
continue
}
} else {
// Edge case: FuncDecl.Name and .Type overlap:
// Don't update best from Name to FuncDecl.Type.
//
// The condition can be read as:
// - n is FuncType
// - n.parent is FuncDecl
// - best is strictly beneath the FuncDecl
if ev.typ == 1<<nFuncType &&
events[ev.parent].typ == 1<<nFuncDecl &&
best > ev.parent {
continue
}
nodeEnd = n.End()
if n.Pos() > start {
break // disjoint, after; stop
}
}
// Inv: node.{Pos,FileStart} <= start
if end <= nodeEnd {
// node fully contains target range
best = i
// Don't search beyond end of the first match.
// This is important only for an empty range (start=end)
// between two adjoining nodes, which would otherwise
// match both nodes; we want to match only the first.
limit = ev.index
} else if nodeEnd < start {
i = ev.index // disjoint, before; skip forward
}
}
}
if best >= 0 {
return Cursor{c.in, best}, true
}
return Cursor{}, false
}

311
vendor/golang.org/x/tools/go/ast/inspector/inspector.go generated vendored Normal file
View File

@@ -0,0 +1,311 @@
// Copyright 2018 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 inspector provides helper functions for traversal over the
// syntax trees of a package, including node filtering by type, and
// materialization of the traversal stack.
//
// During construction, the inspector does a complete traversal and
// builds a list of push/pop events and their node type. Subsequent
// method calls that request a traversal scan this list, rather than walk
// the AST, and perform type filtering using efficient bit sets.
// 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
// 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:
// 1 type filtering
// 2 pruning
// 3 postorder calls to f
// 4 stack
// Rather than offer all of them in the API,
// only a few combinations are exposed:
// - Preorder is the fastest and has fewest features,
// but is the most commonly needed traversal.
// - Nodes and WithStack both provide pruning and postorder calls,
// even though few clients need it, because supporting two versions
// is not justified.
// More combinations could be supported by expressing them as
// wrappers around a more generic traversal, but this was measured
// and found to degrade performance significantly (30%).
import (
"go/ast"
"golang.org/x/tools/go/ast/edge"
)
// An Inspector provides methods for inspecting
// (traversing) the syntax trees of a package.
type Inspector struct {
events []event
}
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.
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
// in the upper 25.
return edge.Kind(x & 0x7f), int(x>>7) - 1
}
// New returns an Inspector for the specified syntax trees.
func New(files []*ast.File) *Inspector {
return &Inspector{traverse(files)}
}
// An event represents a push or a pop
// of an ast.Node during a traversal.
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
parent int32 // index of parent's push node (push nodes only), or packed edge kind/index (pop nodes only)
}
// TODO: Experiment with storing only the second word of event.node (unsafe.Pointer).
// Type can be recovered from the sole bit in typ.
// [Tried this, wasn't faster. --adonovan]
// Preorder visits all the nodes of the files supplied to New in
// 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 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
// features seem to contribute similar slowdowns (~1.4x each).
// This function is equivalent to the PreorderSeq call below,
// but to avoid the additional dynamic call (which adds 13-35%
// to the benchmarks), we expand it out.
//
// in.PreorderSeq(types...)(func(n ast.Node) bool {
// f(n)
// return true
// })
mask := maskOf(types)
for i := int32(0); i < int32(len(in.events)); {
ev := in.events[i]
if ev.index > i {
// push
if ev.typ&mask != 0 {
f(ev.node)
}
pop := ev.index
if in.events[pop].typ&mask == 0 {
// Subtrees do not contain types: skip them and pop.
i = pop + 1
continue
}
}
i++
}
}
// Nodes visits the nodes of the files supplied to New in depth-first
// order. It calls f(n, true) for each node n before it visits n's
// children. If f returns true, Nodes invokes f recursively for each
// 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 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)); {
ev := in.events[i]
if ev.index > i {
// push
pop := ev.index
if ev.typ&mask != 0 {
if !f(ev.node, true) {
i = pop + 1 // jump to corresponding pop + 1
continue
}
}
if in.events[pop].typ&mask == 0 {
// Subtrees do not contain types: skip them.
i = pop
continue
}
} else {
// pop
push := ev.index
if in.events[push].typ&mask != 0 {
f(ev.node, false)
}
}
i++
}
}
// WithStack visits nodes in a similar manner to Nodes, but it
// 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
for i := int32(0); i < int32(len(in.events)); {
ev := in.events[i]
if ev.index > i {
// push
pop := ev.index
stack = append(stack, ev.node)
if ev.typ&mask != 0 {
if !f(ev.node, true, stack) {
i = pop + 1
stack = stack[:len(stack)-1]
continue
}
}
if in.events[pop].typ&mask == 0 {
// Subtrees does not contain types: skip them.
i = pop
continue
}
} else {
// pop
push := ev.index
if in.events[push].typ&mask != 0 {
f(ev.node, false, stack)
}
stack = stack[:len(stack)-1]
}
i++
}
}
// traverse builds the table of events representing a traversal.
func traverse(files []*ast.File) []event {
// Preallocate approximate number of events
// based on source file extent of the declarations.
// (We use End-Pos not FileStart-FileEnd to neglect
// the effect of long doc comments.)
// This makes traverse faster by 4x (!).
var extent int
for _, f := range files {
extent += int(f.End() - f.Pos())
}
// This estimate is based on the net/http package.
capacity := min(extent*33/100, 1e6) // impose some reasonable maximum (1M)
v := &visitor{
events: make([]event, 0, capacity),
stack: []item{{index: -1}}, // include an extra event so file nodes have a parent
}
for _, file := range files {
walk(v, edge.Invalid, -1, file)
}
return v.events
}
type visitor struct {
events []event
stack []item
}
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 descendants
edgeKindAndIndex int32 // edge.Kind and index, bit packed
}
func (v *visitor) push(ek edge.Kind, eindex int, node ast.Node) {
var (
index = int32(len(v.events))
parentIndex = v.stack[len(v.stack)-1].index
)
v.events = append(v.events, event{
node: node,
parent: parentIndex,
typ: typeOf(node),
index: 0, // (pop index is set later by visitor.pop)
})
v.stack = append(v.stack, item{
index: index,
parentIndex: parentIndex,
edgeKindAndIndex: packEdgeKindAndIndex(ek, eindex),
})
// 2B nodes ought to be enough for anyone!
if int32(len(v.events)) < 0 {
panic("event index exceeded int32")
}
// 32M elements in an []ast.Node ought to be enough for anyone!
if ek2, eindex2 := unpackEdgeKindAndIndex(packEdgeKindAndIndex(ek, eindex)); ek2 != ek || eindex2 != eindex {
panic("Node slice index exceeded uint25")
}
}
func (v *visitor) pop(node ast.Node) {
top := len(v.stack) - 1
current := v.stack[top]
push := &v.events[current.index]
parent := &v.stack[top-1]
push.index = int32(len(v.events)) // make push event refer to pop
parent.typAccum |= current.typAccum | push.typ // accumulate type bits into parent
v.stack = v.stack[:top]
v.events = append(v.events, event{
node: node,
typ: current.typAccum,
index: current.index,
parent: current.edgeKindAndIndex, // see [unpackEdgeKindAndIndex]
})
}

85
vendor/golang.org/x/tools/go/ast/inspector/iter.go generated vendored Normal file
View File

@@ -0,0 +1,85 @@
// 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 inspector
import (
"go/ast"
"iter"
)
// PreorderSeq returns an iterator that visits all the
// nodes of the files supplied to New in depth-first order.
// It visits each node n before n's children.
// The complete traversal sequence is determined by ast.Inspect.
//
// The types argument, if non-empty, enables type-based
// filtering of events: only nodes whose type matches an
// element of the types slice are included in the sequence.
func (in *Inspector) PreorderSeq(types ...ast.Node) iter.Seq[ast.Node] {
// This implementation is identical to Preorder,
// except that it supports breaking out of the loop.
return func(yield func(ast.Node) bool) {
mask := maskOf(types)
for i := int32(0); i < int32(len(in.events)); {
ev := in.events[i]
if ev.index > i {
// push
if ev.typ&mask != 0 {
if !yield(ev.node) {
break
}
}
pop := ev.index
if in.events[pop].typ&mask == 0 {
// Subtrees do not contain types: skip them and pop.
i = pop + 1
continue
}
}
i++
}
}
}
// All[N] returns an iterator over all the nodes of type N.
// N must be a pointer-to-struct type that implements ast.Node.
//
// Example:
//
// for call := range All[*ast.CallExpr](in) { ... }
func All[N interface {
*S
ast.Node
}, S any](in *Inspector) iter.Seq[N] {
// To avoid additional dynamic call overheads,
// we duplicate rather than call the logic of PreorderSeq.
mask := typeOf((N)(nil))
return func(yield func(N) bool) {
for i := int32(0); i < int32(len(in.events)); {
ev := in.events[i]
if ev.index > i {
// push
if ev.typ&mask != 0 {
if !yield(ev.node.(N)) {
break
}
}
pop := ev.index
if in.events[pop].typ&mask == 0 {
// Subtrees do not contain types: skip them and pop.
i = pop + 1
continue
}
}
i++
}
}
}

227
vendor/golang.org/x/tools/go/ast/inspector/typeof.go generated vendored Normal file
View File

@@ -0,0 +1,227 @@
// Copyright 2018 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 inspector
// This file defines func typeOf(ast.Node) uint64.
//
// The initial map-based implementation was too slow;
// see https://go-review.googlesource.com/c/tools/+/135655/1/go/ast/inspector/inspector.go#196
import (
"go/ast"
"math"
)
const (
nArrayType = iota
nAssignStmt
nBadDecl
nBadExpr
nBadStmt
nBasicLit
nBinaryExpr
nBlockStmt
nBranchStmt
nCallExpr
nCaseClause
nChanType
nCommClause
nComment
nCommentGroup
nCompositeLit
nDeclStmt
nDeferStmt
nEllipsis
nEmptyStmt
nExprStmt
nField
nFieldList
nFile
nForStmt
nFuncDecl
nFuncLit
nFuncType
nGenDecl
nGoStmt
nIdent
nIfStmt
nImportSpec
nIncDecStmt
nIndexExpr
nIndexListExpr
nInterfaceType
nKeyValueExpr
nLabeledStmt
nMapType
nPackage
nParenExpr
nRangeStmt
nReturnStmt
nSelectStmt
nSelectorExpr
nSendStmt
nSliceExpr
nStarExpr
nStructType
nSwitchStmt
nTypeAssertExpr
nTypeSpec
nTypeSwitchStmt
nUnaryExpr
nValueSpec
)
// typeOf returns a distinct single-bit value that represents the type of n.
//
// Various implementations were benchmarked with BenchmarkNewInspector:
//
// GOGC=off
// - type switch 4.9-5.5ms 2.1ms
// - binary search over a sorted list of types 5.5-5.9ms 2.5ms
// - linear scan, frequency-ordered list 5.9-6.1ms 2.7ms
// - linear scan, unordered list 6.4ms 2.7ms
// - hash table 6.5ms 3.1ms
//
// A perfect hash seemed like overkill.
//
// The compiler's switch statement is the clear winner
// as it produces a binary tree in code,
// with constant conditions and good branch prediction.
// (Sadly it is the most verbose in source code.)
// Binary search suffered from poor branch prediction.
func typeOf(n ast.Node) uint64 {
// Fast path: nearly half of all nodes are identifiers.
if _, ok := n.(*ast.Ident); ok {
return 1 << nIdent
}
// These cases include all nodes encountered by ast.Inspect.
switch n.(type) {
case *ast.ArrayType:
return 1 << nArrayType
case *ast.AssignStmt:
return 1 << nAssignStmt
case *ast.BadDecl:
return 1 << nBadDecl
case *ast.BadExpr:
return 1 << nBadExpr
case *ast.BadStmt:
return 1 << nBadStmt
case *ast.BasicLit:
return 1 << nBasicLit
case *ast.BinaryExpr:
return 1 << nBinaryExpr
case *ast.BlockStmt:
return 1 << nBlockStmt
case *ast.BranchStmt:
return 1 << nBranchStmt
case *ast.CallExpr:
return 1 << nCallExpr
case *ast.CaseClause:
return 1 << nCaseClause
case *ast.ChanType:
return 1 << nChanType
case *ast.CommClause:
return 1 << nCommClause
case *ast.Comment:
return 1 << nComment
case *ast.CommentGroup:
return 1 << nCommentGroup
case *ast.CompositeLit:
return 1 << nCompositeLit
case *ast.DeclStmt:
return 1 << nDeclStmt
case *ast.DeferStmt:
return 1 << nDeferStmt
case *ast.Ellipsis:
return 1 << nEllipsis
case *ast.EmptyStmt:
return 1 << nEmptyStmt
case *ast.ExprStmt:
return 1 << nExprStmt
case *ast.Field:
return 1 << nField
case *ast.FieldList:
return 1 << nFieldList
case *ast.File:
return 1 << nFile
case *ast.ForStmt:
return 1 << nForStmt
case *ast.FuncDecl:
return 1 << nFuncDecl
case *ast.FuncLit:
return 1 << nFuncLit
case *ast.FuncType:
return 1 << nFuncType
case *ast.GenDecl:
return 1 << nGenDecl
case *ast.GoStmt:
return 1 << nGoStmt
case *ast.Ident:
return 1 << nIdent
case *ast.IfStmt:
return 1 << nIfStmt
case *ast.ImportSpec:
return 1 << nImportSpec
case *ast.IncDecStmt:
return 1 << nIncDecStmt
case *ast.IndexExpr:
return 1 << nIndexExpr
case *ast.IndexListExpr:
return 1 << nIndexListExpr
case *ast.InterfaceType:
return 1 << nInterfaceType
case *ast.KeyValueExpr:
return 1 << nKeyValueExpr
case *ast.LabeledStmt:
return 1 << nLabeledStmt
case *ast.MapType:
return 1 << nMapType
case *ast.Package:
return 1 << nPackage
case *ast.ParenExpr:
return 1 << nParenExpr
case *ast.RangeStmt:
return 1 << nRangeStmt
case *ast.ReturnStmt:
return 1 << nReturnStmt
case *ast.SelectStmt:
return 1 << nSelectStmt
case *ast.SelectorExpr:
return 1 << nSelectorExpr
case *ast.SendStmt:
return 1 << nSendStmt
case *ast.SliceExpr:
return 1 << nSliceExpr
case *ast.StarExpr:
return 1 << nStarExpr
case *ast.StructType:
return 1 << nStructType
case *ast.SwitchStmt:
return 1 << nSwitchStmt
case *ast.TypeAssertExpr:
return 1 << nTypeAssertExpr
case *ast.TypeSpec:
return 1 << nTypeSpec
case *ast.TypeSwitchStmt:
return 1 << nTypeSwitchStmt
case *ast.UnaryExpr:
return 1 << nUnaryExpr
case *ast.ValueSpec:
return 1 << nValueSpec
}
return 0
}
func maskOf(nodes []ast.Node) uint64 {
if len(nodes) == 0 {
return math.MaxUint64 // match all node types
}
var mask uint64
for _, n := range nodes {
mask |= typeOf(n)
}
return mask
}

341
vendor/golang.org/x/tools/go/ast/inspector/walk.go generated vendored Normal file
View File

@@ -0,0 +1,341 @@
// 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 inspector
// This file is a fork of ast.Inspect to reduce unnecessary dynamic
// calls and to gather edge information.
//
// Consistency with the original is ensured by TestInspectAllNodes.
import (
"fmt"
"go/ast"
"golang.org/x/tools/go/ast/edge"
)
func walkList[N ast.Node](v *visitor, ek edge.Kind, list []N) {
for i, node := range list {
walk(v, ek, i, node)
}
}
func walk(v *visitor, ek edge.Kind, index int, node ast.Node) {
v.push(ek, index, node)
// walk children
// (the order of the cases matches the order
// of the corresponding node types in ast.go)
switch n := node.(type) {
// Comments and fields
case *ast.Comment:
// nothing to do
case *ast.CommentGroup:
walkList(v, edge.CommentGroup_List, n.List)
case *ast.Field:
if n.Doc != nil {
walk(v, edge.Field_Doc, -1, n.Doc)
}
walkList(v, edge.Field_Names, n.Names)
if n.Type != nil {
walk(v, edge.Field_Type, -1, n.Type)
}
if n.Tag != nil {
walk(v, edge.Field_Tag, -1, n.Tag)
}
if n.Comment != nil {
walk(v, edge.Field_Comment, -1, n.Comment)
}
case *ast.FieldList:
walkList(v, edge.FieldList_List, n.List)
// Expressions
case *ast.BadExpr, *ast.Ident, *ast.BasicLit:
// nothing to do
case *ast.Ellipsis:
if n.Elt != nil {
walk(v, edge.Ellipsis_Elt, -1, n.Elt)
}
case *ast.FuncLit:
walk(v, edge.FuncLit_Type, -1, n.Type)
walk(v, edge.FuncLit_Body, -1, n.Body)
case *ast.CompositeLit:
if n.Type != nil {
walk(v, edge.CompositeLit_Type, -1, n.Type)
}
walkList(v, edge.CompositeLit_Elts, n.Elts)
case *ast.ParenExpr:
walk(v, edge.ParenExpr_X, -1, n.X)
case *ast.SelectorExpr:
walk(v, edge.SelectorExpr_X, -1, n.X)
walk(v, edge.SelectorExpr_Sel, -1, n.Sel)
case *ast.IndexExpr:
walk(v, edge.IndexExpr_X, -1, n.X)
walk(v, edge.IndexExpr_Index, -1, n.Index)
case *ast.IndexListExpr:
walk(v, edge.IndexListExpr_X, -1, n.X)
walkList(v, edge.IndexListExpr_Indices, n.Indices)
case *ast.SliceExpr:
walk(v, edge.SliceExpr_X, -1, n.X)
if n.Low != nil {
walk(v, edge.SliceExpr_Low, -1, n.Low)
}
if n.High != nil {
walk(v, edge.SliceExpr_High, -1, n.High)
}
if n.Max != nil {
walk(v, edge.SliceExpr_Max, -1, n.Max)
}
case *ast.TypeAssertExpr:
walk(v, edge.TypeAssertExpr_X, -1, n.X)
if n.Type != nil {
walk(v, edge.TypeAssertExpr_Type, -1, n.Type)
}
case *ast.CallExpr:
walk(v, edge.CallExpr_Fun, -1, n.Fun)
walkList(v, edge.CallExpr_Args, n.Args)
case *ast.StarExpr:
walk(v, edge.StarExpr_X, -1, n.X)
case *ast.UnaryExpr:
walk(v, edge.UnaryExpr_X, -1, n.X)
case *ast.BinaryExpr:
walk(v, edge.BinaryExpr_X, -1, n.X)
walk(v, edge.BinaryExpr_Y, -1, n.Y)
case *ast.KeyValueExpr:
walk(v, edge.KeyValueExpr_Key, -1, n.Key)
walk(v, edge.KeyValueExpr_Value, -1, n.Value)
// Types
case *ast.ArrayType:
if n.Len != nil {
walk(v, edge.ArrayType_Len, -1, n.Len)
}
walk(v, edge.ArrayType_Elt, -1, n.Elt)
case *ast.StructType:
walk(v, edge.StructType_Fields, -1, n.Fields)
case *ast.FuncType:
if n.TypeParams != nil {
walk(v, edge.FuncType_TypeParams, -1, n.TypeParams)
}
if n.Params != nil {
walk(v, edge.FuncType_Params, -1, n.Params)
}
if n.Results != nil {
walk(v, edge.FuncType_Results, -1, n.Results)
}
case *ast.InterfaceType:
walk(v, edge.InterfaceType_Methods, -1, n.Methods)
case *ast.MapType:
walk(v, edge.MapType_Key, -1, n.Key)
walk(v, edge.MapType_Value, -1, n.Value)
case *ast.ChanType:
walk(v, edge.ChanType_Value, -1, n.Value)
// Statements
case *ast.BadStmt:
// nothing to do
case *ast.DeclStmt:
walk(v, edge.DeclStmt_Decl, -1, n.Decl)
case *ast.EmptyStmt:
// nothing to do
case *ast.LabeledStmt:
walk(v, edge.LabeledStmt_Label, -1, n.Label)
walk(v, edge.LabeledStmt_Stmt, -1, n.Stmt)
case *ast.ExprStmt:
walk(v, edge.ExprStmt_X, -1, n.X)
case *ast.SendStmt:
walk(v, edge.SendStmt_Chan, -1, n.Chan)
walk(v, edge.SendStmt_Value, -1, n.Value)
case *ast.IncDecStmt:
walk(v, edge.IncDecStmt_X, -1, n.X)
case *ast.AssignStmt:
walkList(v, edge.AssignStmt_Lhs, n.Lhs)
walkList(v, edge.AssignStmt_Rhs, n.Rhs)
case *ast.GoStmt:
walk(v, edge.GoStmt_Call, -1, n.Call)
case *ast.DeferStmt:
walk(v, edge.DeferStmt_Call, -1, n.Call)
case *ast.ReturnStmt:
walkList(v, edge.ReturnStmt_Results, n.Results)
case *ast.BranchStmt:
if n.Label != nil {
walk(v, edge.BranchStmt_Label, -1, n.Label)
}
case *ast.BlockStmt:
walkList(v, edge.BlockStmt_List, n.List)
case *ast.IfStmt:
if n.Init != nil {
walk(v, edge.IfStmt_Init, -1, n.Init)
}
walk(v, edge.IfStmt_Cond, -1, n.Cond)
walk(v, edge.IfStmt_Body, -1, n.Body)
if n.Else != nil {
walk(v, edge.IfStmt_Else, -1, n.Else)
}
case *ast.CaseClause:
walkList(v, edge.CaseClause_List, n.List)
walkList(v, edge.CaseClause_Body, n.Body)
case *ast.SwitchStmt:
if n.Init != nil {
walk(v, edge.SwitchStmt_Init, -1, n.Init)
}
if n.Tag != nil {
walk(v, edge.SwitchStmt_Tag, -1, n.Tag)
}
walk(v, edge.SwitchStmt_Body, -1, n.Body)
case *ast.TypeSwitchStmt:
if n.Init != nil {
walk(v, edge.TypeSwitchStmt_Init, -1, n.Init)
}
walk(v, edge.TypeSwitchStmt_Assign, -1, n.Assign)
walk(v, edge.TypeSwitchStmt_Body, -1, n.Body)
case *ast.CommClause:
if n.Comm != nil {
walk(v, edge.CommClause_Comm, -1, n.Comm)
}
walkList(v, edge.CommClause_Body, n.Body)
case *ast.SelectStmt:
walk(v, edge.SelectStmt_Body, -1, n.Body)
case *ast.ForStmt:
if n.Init != nil {
walk(v, edge.ForStmt_Init, -1, n.Init)
}
if n.Cond != nil {
walk(v, edge.ForStmt_Cond, -1, n.Cond)
}
if n.Post != nil {
walk(v, edge.ForStmt_Post, -1, n.Post)
}
walk(v, edge.ForStmt_Body, -1, n.Body)
case *ast.RangeStmt:
if n.Key != nil {
walk(v, edge.RangeStmt_Key, -1, n.Key)
}
if n.Value != nil {
walk(v, edge.RangeStmt_Value, -1, n.Value)
}
walk(v, edge.RangeStmt_X, -1, n.X)
walk(v, edge.RangeStmt_Body, -1, n.Body)
// Declarations
case *ast.ImportSpec:
if n.Doc != nil {
walk(v, edge.ImportSpec_Doc, -1, n.Doc)
}
if n.Name != nil {
walk(v, edge.ImportSpec_Name, -1, n.Name)
}
walk(v, edge.ImportSpec_Path, -1, n.Path)
if n.Comment != nil {
walk(v, edge.ImportSpec_Comment, -1, n.Comment)
}
case *ast.ValueSpec:
if n.Doc != nil {
walk(v, edge.ValueSpec_Doc, -1, n.Doc)
}
walkList(v, edge.ValueSpec_Names, n.Names)
if n.Type != nil {
walk(v, edge.ValueSpec_Type, -1, n.Type)
}
walkList(v, edge.ValueSpec_Values, n.Values)
if n.Comment != nil {
walk(v, edge.ValueSpec_Comment, -1, n.Comment)
}
case *ast.TypeSpec:
if n.Doc != nil {
walk(v, edge.TypeSpec_Doc, -1, n.Doc)
}
walk(v, edge.TypeSpec_Name, -1, n.Name)
if n.TypeParams != nil {
walk(v, edge.TypeSpec_TypeParams, -1, n.TypeParams)
}
walk(v, edge.TypeSpec_Type, -1, n.Type)
if n.Comment != nil {
walk(v, edge.TypeSpec_Comment, -1, n.Comment)
}
case *ast.BadDecl:
// nothing to do
case *ast.GenDecl:
if n.Doc != nil {
walk(v, edge.GenDecl_Doc, -1, n.Doc)
}
walkList(v, edge.GenDecl_Specs, n.Specs)
case *ast.FuncDecl:
if n.Doc != nil {
walk(v, edge.FuncDecl_Doc, -1, n.Doc)
}
if n.Recv != nil {
walk(v, edge.FuncDecl_Recv, -1, n.Recv)
}
walk(v, edge.FuncDecl_Name, -1, n.Name)
walk(v, edge.FuncDecl_Type, -1, n.Type)
if n.Body != nil {
walk(v, edge.FuncDecl_Body, -1, n.Body)
}
case *ast.File:
if n.Doc != nil {
walk(v, edge.File_Doc, -1, n.Doc)
}
walk(v, edge.File_Name, -1, n.Name)
walkList(v, edge.File_Decls, n.Decls)
// don't walk n.Comments - they have been
// visited already through the individual
// nodes
default:
// (includes *ast.Package)
panic(fmt.Sprintf("Walk: unexpected node type %T", n))
}
v.pop(node)
}

193
vendor/golang.org/x/tools/go/buildutil/allpackages.go generated vendored Normal file
View File

@@ -0,0 +1,193 @@
// Copyright 2014 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 buildutil provides utilities related to the go/build
// package in the standard library.
//
// All I/O is done via the build.Context file system interface, which must
// be concurrency-safe.
package buildutil // import "golang.org/x/tools/go/buildutil"
import (
"go/build"
"os"
"path/filepath"
"sort"
"strings"
"sync"
)
// AllPackages returns the package path of each Go package in any source
// directory of the specified build context (e.g. $GOROOT or an element
// of $GOPATH). Errors are ignored. The results are sorted.
// All package paths are canonical, and thus may contain "/vendor/".
//
// The result may include import paths for directories that contain no
// *.go files, such as "archive" (in $GOROOT/src).
//
// All I/O is done via the build.Context file system interface,
// which must be concurrency-safe.
func AllPackages(ctxt *build.Context) []string {
var list []string
ForEachPackage(ctxt, func(pkg string, _ error) {
list = append(list, pkg)
})
sort.Strings(list)
return list
}
// ForEachPackage calls the found function with the package path of
// each Go package it finds in any source directory of the specified
// build context (e.g. $GOROOT or an element of $GOPATH).
// All package paths are canonical, and thus may contain "/vendor/".
//
// If the package directory exists but could not be read, the second
// argument to the found function provides the error.
//
// All I/O is done via the build.Context file system interface,
// which must be concurrency-safe.
func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) {
ch := make(chan item)
var wg sync.WaitGroup
for _, root := range ctxt.SrcDirs() {
wg.Add(1)
go func() {
allPackages(ctxt, root, ch)
wg.Done()
}()
}
go func() {
wg.Wait()
close(ch)
}()
// All calls to found occur in the caller's goroutine.
for i := range ch {
found(i.importPath, i.err)
}
}
type item struct {
importPath string
err error // (optional)
}
// We use a process-wide counting semaphore to limit
// the number of parallel calls to ReadDir.
var ioLimit = make(chan bool, 20)
func allPackages(ctxt *build.Context, root string, ch chan<- item) {
root = filepath.Clean(root) + string(os.PathSeparator)
var wg sync.WaitGroup
var walkDir func(dir string)
walkDir = func(dir string) {
// Avoid .foo, _foo, and testdata directory trees.
base := filepath.Base(dir)
if base == "" || base[0] == '.' || base[0] == '_' || base == "testdata" {
return
}
pkg := filepath.ToSlash(strings.TrimPrefix(dir, root))
// Prune search if we encounter any of these import paths.
switch pkg {
case "builtin":
return
}
ioLimit <- true
files, err := ReadDir(ctxt, dir)
<-ioLimit
if pkg != "" || err != nil {
ch <- item{pkg, err}
}
for _, fi := range files {
if fi.IsDir() {
wg.Add(1)
go func() {
walkDir(filepath.Join(dir, fi.Name()))
wg.Done()
}()
}
}
}
walkDir(root)
wg.Wait()
}
// ExpandPatterns returns the set of packages matched by patterns,
// which may have the following forms:
//
// golang.org/x/tools/cmd/guru # a single package
// golang.org/x/tools/... # all packages beneath dir
// ... # the entire workspace.
//
// Order is significant: a pattern preceded by '-' removes matching
// packages from the set. For example, these patterns match all encoding
// packages except encoding/xml:
//
// encoding/... -encoding/xml
//
// A trailing slash in a pattern is ignored. (Path components of Go
// package names are separated by slash, not the platform's path separator.)
func ExpandPatterns(ctxt *build.Context, patterns []string) map[string]bool {
// TODO(adonovan): support other features of 'go list':
// - "std"/"cmd"/"all" meta-packages
// - "..." not at the end of a pattern
// - relative patterns using "./" or "../" prefix
pkgs := make(map[string]bool)
doPkg := func(pkg string, neg bool) {
if neg {
delete(pkgs, pkg)
} else {
pkgs[pkg] = true
}
}
// Scan entire workspace if wildcards are present.
// TODO(adonovan): opt: scan only the necessary subtrees of the workspace.
var all []string
for _, arg := range patterns {
if strings.HasSuffix(arg, "...") {
all = AllPackages(ctxt)
break
}
}
for _, arg := range patterns {
if arg == "" {
continue
}
neg := arg[0] == '-'
if neg {
arg = arg[1:]
}
if arg == "..." {
// ... matches all packages
for _, pkg := range all {
doPkg(pkg, neg)
}
} else if dir, ok := strings.CutSuffix(arg, "/..."); ok {
// dir/... matches all packages beneath dir
for _, pkg := range all {
if strings.HasPrefix(pkg, dir) &&
(len(pkg) == len(dir) || pkg[len(dir)] == '/') {
doPkg(pkg, neg)
}
}
} else {
// single package
doPkg(strings.TrimSuffix(arg, "/"), neg)
}
}
return pkgs
}

111
vendor/golang.org/x/tools/go/buildutil/fakecontext.go generated vendored Normal file
View File

@@ -0,0 +1,111 @@
// 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.
package buildutil
import (
"fmt"
"go/build"
"io"
"os"
"path"
"path/filepath"
"sort"
"strings"
"time"
)
// FakeContext returns a build.Context for the fake file tree specified
// by pkgs, which maps package import paths to a mapping from file base
// names to contents.
//
// The fake Context has a GOROOT of "/go" and no GOPATH, and overrides
// the necessary file access methods to read from memory instead of the
// real file system.
//
// Unlike a real file tree, the fake one has only two levels---packages
// and files---so ReadDir("/go/src/") returns all packages under
// /go/src/ including, for instance, "math" and "math/big".
// ReadDir("/go/src/math/big") would return all the files in the
// "math/big" package.
func FakeContext(pkgs map[string]map[string]string) *build.Context {
clean := func(filename string) string {
f := path.Clean(filepath.ToSlash(filename))
// Removing "/go/src" while respecting segment
// boundaries has this unfortunate corner case:
if f == "/go/src" {
return ""
}
return strings.TrimPrefix(f, "/go/src/")
}
ctxt := build.Default // copy
ctxt.GOROOT = "/go"
ctxt.GOPATH = ""
ctxt.Compiler = "gc"
ctxt.IsDir = func(dir string) bool {
dir = clean(dir)
if dir == "" {
return true // needed by (*build.Context).SrcDirs
}
return pkgs[dir] != nil
}
ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) {
dir = clean(dir)
var fis []os.FileInfo
if dir == "" {
// enumerate packages
for importPath := range pkgs {
fis = append(fis, fakeDirInfo(importPath))
}
} else {
// enumerate files of package
for basename := range pkgs[dir] {
fis = append(fis, fakeFileInfo(basename))
}
}
sort.Sort(byName(fis))
return fis, nil
}
ctxt.OpenFile = func(filename string) (io.ReadCloser, error) {
filename = clean(filename)
dir, base := path.Split(filename)
content, ok := pkgs[path.Clean(dir)][base]
if !ok {
return nil, fmt.Errorf("file not found: %s", filename)
}
return io.NopCloser(strings.NewReader(content)), nil
}
ctxt.IsAbsPath = func(path string) bool {
path = filepath.ToSlash(path)
// Don't rely on the default (filepath.Path) since on
// Windows, it reports virtual paths as non-absolute.
return strings.HasPrefix(path, "/")
}
return &ctxt
}
type byName []os.FileInfo
func (s byName) Len() int { return len(s) }
func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
type fakeFileInfo string
func (fi fakeFileInfo) Name() string { return string(fi) }
func (fakeFileInfo) Sys() any { return nil }
func (fakeFileInfo) ModTime() time.Time { return time.Time{} }
func (fakeFileInfo) IsDir() bool { return false }
func (fakeFileInfo) Size() int64 { return 0 }
func (fakeFileInfo) Mode() os.FileMode { return 0644 }
type fakeDirInfo string
func (fd fakeDirInfo) Name() string { return string(fd) }
func (fakeDirInfo) Sys() any { return nil }
func (fakeDirInfo) ModTime() time.Time { return time.Time{} }
func (fakeDirInfo) IsDir() bool { return true }
func (fakeDirInfo) Size() int64 { return 0 }
func (fakeDirInfo) Mode() os.FileMode { return 0755 }

101
vendor/golang.org/x/tools/go/buildutil/overlay.go generated vendored Normal file
View File

@@ -0,0 +1,101 @@
// Copyright 2016 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 buildutil
import (
"bufio"
"bytes"
"fmt"
"go/build"
"io"
"path/filepath"
"strconv"
"strings"
)
// OverlayContext overlays a build.Context with additional files from
// a map. Files in the map take precedence over other files.
//
// In addition to plain string comparison, two file names are
// considered equal if their base names match and their directory
// components point at the same directory on the file system. That is,
// symbolic links are followed for directories, but not files.
//
// A common use case for OverlayContext is to allow editors to pass in
// a set of unsaved, modified files.
//
// Currently, only the Context.OpenFile function will respect the
// overlay. This may change in the future.
func OverlayContext(orig *build.Context, overlay map[string][]byte) *build.Context {
// TODO(dominikh): Implement IsDir, HasSubdir and ReadDir
rc := func(data []byte) (io.ReadCloser, error) {
return io.NopCloser(bytes.NewBuffer(data)), nil
}
copy := *orig // make a copy
ctxt := &copy
ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
// Fast path: names match exactly.
if content, ok := overlay[path]; ok {
return rc(content)
}
// Slow path: check for same file under a different
// alias, perhaps due to a symbolic link.
for filename, content := range overlay {
if sameFile(path, filename) {
return rc(content)
}
}
return OpenFile(orig, path)
}
return ctxt
}
// ParseOverlayArchive parses an archive containing Go files and their
// contents. The result is intended to be used with OverlayContext.
//
// # Archive format
//
// The archive consists of a series of files. Each file consists of a
// name, a decimal file size and the file contents, separated by
// newlines. No newline follows after the file contents.
func ParseOverlayArchive(archive io.Reader) (map[string][]byte, error) {
overlay := make(map[string][]byte)
r := bufio.NewReader(archive)
for {
// Read file name.
filename, err := r.ReadString('\n')
if err != nil {
if err == io.EOF {
break // OK
}
return nil, fmt.Errorf("reading archive file name: %v", err)
}
filename = filepath.Clean(strings.TrimSpace(filename))
// Read file size.
sz, err := r.ReadString('\n')
if err != nil {
return nil, fmt.Errorf("reading size of archive file %s: %v", filename, err)
}
sz = strings.TrimSpace(sz)
size, err := strconv.ParseUint(sz, 10, 32)
if err != nil {
return nil, fmt.Errorf("parsing size of archive file %s: %v", filename, err)
}
// Read file content.
content := make([]byte, size)
if _, err := io.ReadFull(r, content); err != nil {
return nil, fmt.Errorf("reading archive file %s: %v", filename, err)
}
overlay[filename] = content
}
return overlay, nil
}

100
vendor/golang.org/x/tools/go/buildutil/tags.go generated vendored Normal file
View File

@@ -0,0 +1,100 @@
// 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.
package buildutil
// This duplicated logic must be kept in sync with that from go build:
// $GOROOT/src/cmd/go/internal/work/build.go (tagsFlag.Set)
// $GOROOT/src/cmd/go/internal/base/flag.go (StringsFlag.Set)
// $GOROOT/src/cmd/internal/quoted/quoted.go (isSpaceByte, Split)
import (
"fmt"
"strings"
)
const TagsFlagDoc = "a list of `build tags` to consider satisfied during the build. " +
"For more information about build tags, see the description of " +
"build constraints in the documentation for the go/build package"
// TagsFlag is an implementation of the flag.Value and flag.Getter interfaces that parses
// a flag value the same as go build's -tags flag and populates a []string slice.
//
// See $GOROOT/src/go/build/doc.go for description of build tags.
// See $GOROOT/src/cmd/go/doc.go for description of 'go build -tags' flag.
//
// Example:
//
// flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
type TagsFlag []string
func (v *TagsFlag) Set(s string) error {
// See $GOROOT/src/cmd/go/internal/work/build.go (tagsFlag.Set)
// For compatibility with Go 1.12 and earlier, allow "-tags='a b c'" or even just "-tags='a'".
if strings.Contains(s, " ") || strings.Contains(s, "'") {
var err error
*v, err = splitQuotedFields(s)
if *v == nil {
*v = []string{}
}
return err
}
// Starting in Go 1.13, the -tags flag is a comma-separated list of build tags.
*v = []string{}
for s := range strings.SplitSeq(s, ",") {
if s != "" {
*v = append(*v, s)
}
}
return nil
}
func (v *TagsFlag) Get() any { return *v }
func splitQuotedFields(s string) ([]string, error) {
// See $GOROOT/src/cmd/internal/quoted/quoted.go (Split)
// This must remain in sync with that logic.
var f []string
for len(s) > 0 {
for len(s) > 0 && isSpaceByte(s[0]) {
s = s[1:]
}
if len(s) == 0 {
break
}
// Accepted quoted string. No unescaping inside.
if s[0] == '"' || s[0] == '\'' {
quote := s[0]
s = s[1:]
i := 0
for i < len(s) && s[i] != quote {
i++
}
if i >= len(s) {
return nil, fmt.Errorf("unterminated %c string", quote)
}
f = append(f, s[:i])
s = s[i+1:]
continue
}
i := 0
for i < len(s) && !isSpaceByte(s[i]) {
i++
}
f = append(f, s[:i])
s = s[i:]
}
return f, nil
}
func (v *TagsFlag) String() string {
return "<tagsFlag>"
}
func isSpaceByte(c byte) bool {
// See $GOROOT/src/cmd/internal/quoted/quoted.go (isSpaceByte, Split)
// This list must remain in sync with that.
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}

209
vendor/golang.org/x/tools/go/buildutil/util.go generated vendored Normal file
View File

@@ -0,0 +1,209 @@
// Copyright 2014 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 buildutil
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
)
// ParseFile behaves like parser.ParseFile,
// but uses the build context's file system interface, if any.
//
// If file is not absolute (as defined by IsAbsPath), the (dir, file)
// components are joined using JoinPath; dir must be absolute.
//
// The displayPath function, if provided, is used to transform the
// filename that will be attached to the ASTs.
//
// TODO(adonovan): call this from go/loader.parseFiles when the tree thaws.
func ParseFile(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, file string, mode parser.Mode) (*ast.File, error) {
if !IsAbsPath(ctxt, file) {
file = JoinPath(ctxt, dir, file)
}
rd, err := OpenFile(ctxt, file)
if err != nil {
return nil, err
}
defer rd.Close() // ignore error
if displayPath != nil {
file = displayPath(file)
}
return parser.ParseFile(fset, file, rd, mode)
}
// ContainingPackage returns the package containing filename.
//
// If filename is not absolute, it is interpreted relative to working directory dir.
// All I/O is via the build context's file system interface, if any.
//
// The '...Files []string' fields of the resulting build.Package are not
// populated (build.FindOnly mode).
func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Package, error) {
if !IsAbsPath(ctxt, filename) {
filename = JoinPath(ctxt, dir, filename)
}
// We must not assume the file tree uses
// "/" always,
// `\` always,
// or os.PathSeparator (which varies by platform),
// but to make any progress, we are forced to assume that
// paths will not use `\` unless the PathSeparator
// is also `\`, thus we can rely on filepath.ToSlash for some sanity.
dirSlash := path.Dir(filepath.ToSlash(filename)) + "/"
// We assume that no source root (GOPATH[i] or GOROOT) contains any other.
for _, srcdir := range ctxt.SrcDirs() {
srcdirSlash := filepath.ToSlash(srcdir) + "/"
if importPath, ok := HasSubdir(ctxt, srcdirSlash, dirSlash); ok {
return ctxt.Import(importPath, dir, build.FindOnly)
}
}
return nil, fmt.Errorf("can't find package containing %s", filename)
}
// -- Effective methods of file system interface -------------------------
// (go/build.Context defines these as methods, but does not export them.)
// HasSubdir calls ctxt.HasSubdir (if not nil) or else uses
// the local file system to answer the question.
func HasSubdir(ctxt *build.Context, root, dir string) (rel string, ok bool) {
if f := ctxt.HasSubdir; f != nil {
return f(root, dir)
}
// Try using paths we received.
if rel, ok = hasSubdir(root, dir); ok {
return
}
// Try expanding symlinks and comparing
// expanded against unexpanded and
// expanded against expanded.
rootSym, _ := filepath.EvalSymlinks(root)
dirSym, _ := filepath.EvalSymlinks(dir)
if rel, ok = hasSubdir(rootSym, dir); ok {
return
}
if rel, ok = hasSubdir(root, dirSym); ok {
return
}
return hasSubdir(rootSym, dirSym)
}
func hasSubdir(root, dir string) (rel string, ok bool) {
const sep = string(filepath.Separator)
root = filepath.Clean(root)
if !strings.HasSuffix(root, sep) {
root += sep
}
dir = filepath.Clean(dir)
if !strings.HasPrefix(dir, root) {
return "", false
}
return filepath.ToSlash(dir[len(root):]), true
}
// FileExists returns true if the specified file exists,
// using the build context's file system interface.
func FileExists(ctxt *build.Context, path string) bool {
if ctxt.OpenFile != nil {
r, err := ctxt.OpenFile(path)
if err != nil {
return false
}
r.Close() // ignore error
return true
}
_, err := os.Stat(path)
return err == nil
}
// OpenFile behaves like os.Open,
// but uses the build context's file system interface, if any.
func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) {
if ctxt.OpenFile != nil {
return ctxt.OpenFile(path)
}
return os.Open(path)
}
// IsAbsPath behaves like filepath.IsAbs,
// but uses the build context's file system interface, if any.
func IsAbsPath(ctxt *build.Context, path string) bool {
if ctxt.IsAbsPath != nil {
return ctxt.IsAbsPath(path)
}
return filepath.IsAbs(path)
}
// JoinPath behaves like filepath.Join,
// but uses the build context's file system interface, if any.
func JoinPath(ctxt *build.Context, path ...string) string {
if ctxt.JoinPath != nil {
return ctxt.JoinPath(path...)
}
return filepath.Join(path...)
}
// IsDir behaves like os.Stat plus IsDir,
// but uses the build context's file system interface, if any.
func IsDir(ctxt *build.Context, path string) bool {
if ctxt.IsDir != nil {
return ctxt.IsDir(path)
}
fi, err := os.Stat(path)
return err == nil && fi.IsDir()
}
// ReadDir behaves like ioutil.ReadDir,
// but uses the build context's file system interface, if any.
func ReadDir(ctxt *build.Context, path string) ([]os.FileInfo, error) {
if ctxt.ReadDir != nil {
return ctxt.ReadDir(path)
}
return ioutil.ReadDir(path)
}
// SplitPathList behaves like filepath.SplitList,
// but uses the build context's file system interface, if any.
func SplitPathList(ctxt *build.Context, s string) []string {
if ctxt.SplitPathList != nil {
return ctxt.SplitPathList(s)
}
return filepath.SplitList(s)
}
// sameFile returns true if x and y have the same basename and denote
// the same file.
func sameFile(x, y string) bool {
if path.Clean(x) == path.Clean(y) {
return true
}
if filepath.Base(x) == filepath.Base(y) { // (optimisation)
if xi, err := os.Stat(x); err == nil {
if yi, err := os.Stat(y); err == nil {
return os.SameFile(xi, yi)
}
}
}
return false
}

124
vendor/golang.org/x/tools/go/callgraph/callgraph.go generated vendored Normal file
View File

@@ -0,0 +1,124 @@
// Copyright 2013 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 callgraph defines the call graph and various algorithms
and utilities to operate on it.
A call graph is a labelled directed graph whose nodes represent
functions and whose edge labels represent syntactic function call
sites. The presence of a labelled edge (caller, site, callee)
indicates that caller may call callee at the specified call site.
A call graph is a multigraph: it may contain multiple edges (caller,
*, callee) connecting the same pair of nodes, so long as the edges
differ by label; this occurs when one function calls another function
from multiple call sites. Also, it may contain multiple edges
(caller, site, *) that differ only by callee; this indicates a
polymorphic call.
A SOUND call graph is one that overapproximates the dynamic calling
behaviors of the program in all possible executions. One call graph
is more PRECISE than another if it is a smaller overapproximation of
the dynamic behavior.
All call graphs have a synthetic root node which is responsible for
calling main() and init().
Calls to built-in functions (e.g. panic, println) are not represented
in the call graph; they are treated like built-in operators of the
language.
*/
package callgraph // import "golang.org/x/tools/go/callgraph"
// TODO(zpavlinovic): decide how callgraphs handle calls to and from generic function bodies.
import (
"fmt"
"go/token"
"golang.org/x/tools/go/ssa"
)
// A Graph represents a call graph.
//
// A graph may contain nodes that are not reachable from the root.
// If the call graph is sound, such nodes indicate unreachable
// functions.
type Graph struct {
Root *Node // the distinguished root node (Root.Func may be nil)
Nodes map[*ssa.Function]*Node // all nodes by function
}
// New returns a new Graph with the specified (optional) root node.
func New(root *ssa.Function) *Graph {
g := &Graph{Nodes: make(map[*ssa.Function]*Node)}
g.Root = g.CreateNode(root)
return g
}
// CreateNode returns the Node for fn, creating it if not present.
// The root node may have fn=nil.
func (g *Graph) CreateNode(fn *ssa.Function) *Node {
n, ok := g.Nodes[fn]
if !ok {
n = &Node{Func: fn, ID: len(g.Nodes)}
g.Nodes[fn] = n
}
return n
}
// A Node represents a node in a call graph.
type Node struct {
Func *ssa.Function // the function this node represents
ID int // 0-based sequence number
In []*Edge // unordered set of incoming call edges (n.In[*].Callee == n)
Out []*Edge // unordered set of outgoing call edges (n.Out[*].Caller == n)
}
func (n *Node) String() string {
return fmt.Sprintf("n%d:%s", n.ID, n.Func)
}
// A Edge represents an edge in the call graph.
//
// Site is nil for edges originating in synthetic or intrinsic
// functions, e.g. reflect.Value.Call or the root of the call graph.
type Edge struct {
Caller *Node
Site ssa.CallInstruction
Callee *Node
}
func (e Edge) String() string {
return fmt.Sprintf("%s --> %s", e.Caller, e.Callee)
}
func (e Edge) Description() string {
var prefix string
switch e.Site.(type) {
case nil:
return "synthetic call"
case *ssa.Go:
prefix = "concurrent "
case *ssa.Defer:
prefix = "deferred "
}
return prefix + e.Site.Common().Description()
}
func (e Edge) Pos() token.Pos {
if e.Site == nil {
return token.NoPos
}
return e.Site.Pos()
}
// AddEdge adds the edge (caller, site, callee) to the call graph.
// Elimination of duplicate edges is the caller's responsibility.
func AddEdge(caller *Node, site ssa.CallInstruction, callee *Node) {
e := &Edge{caller, site, callee}
callee.In = append(callee.In, e)
caller.Out = append(caller.Out, e)
}

77
vendor/golang.org/x/tools/go/callgraph/cha/cha.go generated vendored Normal file
View File

@@ -0,0 +1,77 @@
// Copyright 2014 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 cha computes the call graph of a Go program using the Class
// Hierarchy Analysis (CHA) algorithm.
//
// CHA was first described in "Optimization of Object-Oriented Programs
// Using Static Class Hierarchy Analysis", Jeffrey Dean, David Grove,
// and Craig Chambers, ECOOP'95.
//
// CHA is related to RTA (see go/callgraph/rta); the difference is that
// CHA conservatively computes the entire "implements" relation between
// interfaces and concrete types ahead of time, whereas RTA uses dynamic
// programming to construct it on the fly as it encounters new functions
// reachable from main. CHA may thus include spurious call edges for
// types that haven't been instantiated yet, or types that are never
// instantiated.
//
// Since CHA conservatively assumes that all functions are address-taken
// and all concrete types are put into interfaces, it is sound to run on
// partial programs, such as libraries without a main or test function.
package cha // import "golang.org/x/tools/go/callgraph/cha"
// TODO(zpavlinovic): update CHA for how it handles generic function bodies.
import (
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/internal/chautil"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
// CallGraph computes the call graph of the specified program using the
// Class Hierarchy Analysis algorithm.
func CallGraph(prog *ssa.Program) *callgraph.Graph {
cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph
allFuncs := ssautil.AllFunctions(prog)
calleesOf := lazyCallees(allFuncs)
addEdge := func(fnode *callgraph.Node, site ssa.CallInstruction, g *ssa.Function) {
gnode := cg.CreateNode(g)
callgraph.AddEdge(fnode, site, gnode)
}
addEdges := func(fnode *callgraph.Node, site ssa.CallInstruction, callees []*ssa.Function) {
// Because every call to a highly polymorphic and
// frequently used abstract method such as
// (io.Writer).Write is assumed to call every concrete
// Write method in the program, the call graph can
// contain a lot of duplication.
for _, g := range callees {
addEdge(fnode, site, g)
}
}
for f := range allFuncs {
fnode := cg.CreateNode(f)
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
if site, ok := instr.(ssa.CallInstruction); ok {
if g := site.Common().StaticCallee(); g != nil {
addEdge(fnode, site, g)
} else {
addEdges(fnode, site, calleesOf(site))
}
}
}
}
}
return cg
}
var lazyCallees = chautil.LazyCallees

View File

@@ -0,0 +1,96 @@
// 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.
// Package chautil provides helper functions related to
// class hierarchy analysis (CHA) for use in x/tools.
package chautil
import (
"go/types"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types/typeutil"
)
// LazyCallees returns a function that maps a call site (in a function in fns)
// to its callees within fns. The set of callees is computed using the CHA algorithm,
// i.e., on the entire implements relation between interfaces and concrete types
// in fns. Please see golang.org/x/tools/go/callgraph/cha for more information.
//
// The resulting function is not concurrency safe.
func LazyCallees(fns map[*ssa.Function]bool) func(site ssa.CallInstruction) []*ssa.Function {
// funcsBySig contains all functions, keyed by signature. It is
// the effective set of address-taken functions used to resolve
// a dynamic call of a particular signature.
var funcsBySig typeutil.Map // value is []*ssa.Function
// methodsByID contains all methods, grouped by ID for efficient
// lookup.
//
// We must key by ID, not name, for correct resolution of interface
// calls to a type with two (unexported) methods spelled the same but
// from different packages. The fact that the concrete type implements
// the interface does not mean the call dispatches to both methods.
methodsByID := make(map[string][]*ssa.Function)
// An imethod represents an interface method I.m.
// (There's no go/types object for it;
// a *types.Func may be shared by many interfaces due to interface embedding.)
type imethod struct {
I *types.Interface
id string
}
// methodsMemo records, for every abstract method call I.m on
// interface type I, the set of concrete methods C.m of all
// types C that satisfy interface I.
//
// Abstract methods may be shared by several interfaces,
// hence we must pass I explicitly, not guess from m.
//
// methodsMemo is just a cache, so it needn't be a typeutil.Map.
methodsMemo := make(map[imethod][]*ssa.Function)
lookupMethods := func(I *types.Interface, m *types.Func) []*ssa.Function {
id := m.Id()
methods, ok := methodsMemo[imethod{I, id}]
if !ok {
for _, f := range methodsByID[id] {
C := f.Signature.Recv().Type() // named or *named
if types.Implements(C, I) {
methods = append(methods, f)
}
}
methodsMemo[imethod{I, id}] = methods
}
return methods
}
for f := range fns {
if f.Signature.Recv() == nil {
// Package initializers can never be address-taken.
if f.Name() == "init" && f.Synthetic == "package initializer" {
continue
}
funcs, _ := funcsBySig.At(f.Signature).([]*ssa.Function)
funcs = append(funcs, f)
funcsBySig.Set(f.Signature, funcs)
} else if obj := f.Object(); obj != nil {
id := obj.(*types.Func).Id()
methodsByID[id] = append(methodsByID[id], f)
}
}
return func(site ssa.CallInstruction) []*ssa.Function {
call := site.Common()
if call.IsInvoke() {
tiface := call.Value.Type().Underlying().(*types.Interface)
return lookupMethods(tiface, call.Method)
} else if g := call.StaticCallee(); g != nil {
return []*ssa.Function{g}
} else if _, ok := call.Value.(*ssa.Builtin); !ok {
fns, _ := funcsBySig.At(call.Signature()).([]*ssa.Function)
return fns
}
return nil
}
}

563
vendor/golang.org/x/tools/go/callgraph/rta/rta.go generated vendored Normal file
View File

@@ -0,0 +1,563 @@
// Copyright 2013 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.
// This package provides Rapid Type Analysis (RTA) for Go, a fast
// algorithm for call graph construction and discovery of reachable code
// (and hence dead code) and runtime types. The algorithm was first
// described in:
//
// David F. Bacon and Peter F. Sweeney. 1996.
// Fast static analysis of C++ virtual function calls. (OOPSLA '96)
// http://doi.acm.org/10.1145/236337.236371
//
// The algorithm uses dynamic programming to tabulate the cross-product
// of the set of known "address-taken" functions with the set of known
// dynamic calls of the same type. As each new address-taken function
// is discovered, call graph edges are added from each known callsite,
// and as each new call site is discovered, call graph edges are added
// from it to each known address-taken function.
//
// A similar approach is used for dynamic calls via interfaces: it
// tabulates the cross-product of the set of known "runtime types",
// i.e. types that may appear in an interface value, or may be derived from
// one via reflection, with the set of known "invoke"-mode dynamic
// calls. As each new runtime type is discovered, call edges are
// added from the known call sites, and as each new call site is
// discovered, call graph edges are added to each compatible
// method.
//
// In addition, we must consider as reachable all address-taken
// functions and all exported methods of any runtime type, since they
// may be called via reflection.
//
// Each time a newly added call edge causes a new function to become
// reachable, the code of that function is analyzed for more call sites,
// address-taken functions, and runtime types. The process continues
// until a fixed point is reached.
package rta // import "golang.org/x/tools/go/callgraph/rta"
import (
"fmt"
"go/types"
"hash/crc32"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types/typeutil"
)
// A Result holds the results of Rapid Type Analysis, which includes the
// set of reachable functions/methods, runtime types, and the call graph.
type Result struct {
// CallGraph is the discovered callgraph.
// It does not include edges for calls made via reflection.
CallGraph *callgraph.Graph
// Reachable contains the set of reachable functions and methods.
// This includes exported methods of runtime types, since
// they may be accessed via reflection.
// The value indicates whether the function is address-taken.
//
// (We wrap the bool in a struct to avoid inadvertent use of
// "if Reachable[f] {" to test for set membership.)
Reachable map[*ssa.Function]struct{ AddrTaken bool }
// RuntimeTypes contains the set of types that are needed at
// runtime, for interfaces or reflection.
//
// The value indicates whether the type is inaccessible to reflection.
// Consider:
// type A struct{B}
// fmt.Println(new(A))
// Types *A, A and B are accessible to reflection, but the unnamed
// type struct{B} is not.
RuntimeTypes typeutil.Map
}
// Working state of the RTA algorithm.
type rta struct {
result *Result
prog *ssa.Program
reflectValueCall *ssa.Function // (*reflect.Value).Call, iff part of prog
worklist []*ssa.Function // list of functions to visit
// addrTakenFuncsBySig contains all address-taken *Functions, grouped by signature.
// Keys are *types.Signature, values are map[*ssa.Function]bool sets.
addrTakenFuncsBySig typeutil.Map
// dynCallSites contains all dynamic "call"-mode call sites, grouped by signature.
// Keys are *types.Signature, values are unordered []ssa.CallInstruction.
dynCallSites typeutil.Map
// invokeSites contains all "invoke"-mode call sites, grouped by interface.
// Keys are *types.Interface (never *types.Named),
// Values are unordered []ssa.CallInstruction sets.
invokeSites typeutil.Map
// The following two maps together define the subset of the
// m:n "implements" relation needed by the algorithm.
// concreteTypes maps each concrete type to information about it.
// Keys are types.Type, values are *concreteTypeInfo.
// Only concrete types used as MakeInterface operands are included.
concreteTypes typeutil.Map
// interfaceTypes maps each interface type to information about it.
// Keys are *types.Interface, values are *interfaceTypeInfo.
// Only interfaces used in "invoke"-mode CallInstructions are included.
interfaceTypes typeutil.Map
}
type concreteTypeInfo struct {
C types.Type
mset *types.MethodSet
fprint uint64 // fingerprint of method set
implements []*types.Interface // unordered set of implemented interfaces
}
type interfaceTypeInfo struct {
I *types.Interface
mset *types.MethodSet
fprint uint64
implementations []types.Type // unordered set of concrete implementations
}
// addReachable marks a function as potentially callable at run-time,
// and ensures that it gets processed.
func (r *rta) addReachable(f *ssa.Function, addrTaken bool) {
reachable := r.result.Reachable
n := len(reachable)
v := reachable[f]
if addrTaken {
v.AddrTaken = true
}
reachable[f] = v
if len(reachable) > n {
// First time seeing f. Add it to the worklist.
r.worklist = append(r.worklist, f)
}
}
// addEdge adds the specified call graph edge, and marks it reachable.
// addrTaken indicates whether to mark the callee as "address-taken".
// site is nil for calls made via reflection.
func (r *rta) addEdge(caller *ssa.Function, site ssa.CallInstruction, callee *ssa.Function, addrTaken bool) {
r.addReachable(callee, addrTaken)
if g := r.result.CallGraph; g != nil {
if caller == nil {
panic(site)
}
from := g.CreateNode(caller)
to := g.CreateNode(callee)
callgraph.AddEdge(from, site, to)
}
}
// ---------- addrTakenFuncs × dynCallSites ----------
// visitAddrTakenFunc is called each time we encounter an address-taken function f.
func (r *rta) visitAddrTakenFunc(f *ssa.Function) {
// Create two-level map (Signature -> Function -> bool).
S := f.Signature
funcs, _ := r.addrTakenFuncsBySig.At(S).(map[*ssa.Function]bool)
if funcs == nil {
funcs = make(map[*ssa.Function]bool)
r.addrTakenFuncsBySig.Set(S, funcs)
}
if !funcs[f] {
// First time seeing f.
funcs[f] = true
// If we've seen any dyncalls of this type, mark it reachable,
// and add call graph edges.
sites, _ := r.dynCallSites.At(S).([]ssa.CallInstruction)
for _, site := range sites {
r.addEdge(site.Parent(), site, f, true)
}
// If the program includes (*reflect.Value).Call,
// add a dynamic call edge from it to any address-taken
// function, regardless of signature.
//
// This isn't perfect.
// - The actual call comes from an internal function
// called reflect.call, but we can't rely on that here.
// - reflect.Value.CallSlice behaves similarly,
// but we don't bother to create callgraph edges from
// it as well as it wouldn't fundamentally change the
// reachability but it would add a bunch more edges.
// - We assume that if reflect.Value.Call is among
// the dependencies of the application, it is itself
// reachable. (It would be more accurate to defer
// all the addEdges below until r.V.Call itself
// becomes reachable.)
// - Fake call graph edges are added from r.V.Call to
// each address-taken function, but not to every
// method reachable through a materialized rtype,
// which is a little inconsistent. Still, the
// reachable set includes both kinds, which is what
// matters for e.g. deadcode detection.)
if r.reflectValueCall != nil {
var site ssa.CallInstruction = nil // can't find actual call site
r.addEdge(r.reflectValueCall, site, f, true)
}
}
}
// visitDynCall is called each time we encounter a dynamic "call"-mode call.
func (r *rta) visitDynCall(site ssa.CallInstruction) {
S := site.Common().Signature()
// Record the call site.
sites, _ := r.dynCallSites.At(S).([]ssa.CallInstruction)
r.dynCallSites.Set(S, append(sites, site))
// For each function of signature S that we know is address-taken,
// add an edge and mark it reachable.
funcs, _ := r.addrTakenFuncsBySig.At(S).(map[*ssa.Function]bool)
for g := range funcs {
r.addEdge(site.Parent(), site, g, true)
}
}
// ---------- concrete types × invoke sites ----------
// addInvokeEdge is called for each new pair (site, C) in the matrix.
func (r *rta) addInvokeEdge(site ssa.CallInstruction, C types.Type) {
// Ascertain the concrete method of C to be called.
imethod := site.Common().Method
cmethod := r.prog.LookupMethod(C, imethod.Pkg(), imethod.Name())
r.addEdge(site.Parent(), site, cmethod, true)
}
// visitInvoke is called each time the algorithm encounters an "invoke"-mode call.
func (r *rta) visitInvoke(site ssa.CallInstruction) {
I := site.Common().Value.Type().Underlying().(*types.Interface)
// Record the invoke site.
sites, _ := r.invokeSites.At(I).([]ssa.CallInstruction)
r.invokeSites.Set(I, append(sites, site))
// Add callgraph edge for each existing
// address-taken concrete type implementing I.
for _, C := range r.implementations(I) {
r.addInvokeEdge(site, C)
}
}
// ---------- main algorithm ----------
// visitFunc processes function f.
func (r *rta) visitFunc(f *ssa.Function) {
var space [32]*ssa.Value // preallocate space for common case
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
rands := instr.Operands(space[:0])
switch instr := instr.(type) {
case ssa.CallInstruction:
call := instr.Common()
if call.IsInvoke() {
r.visitInvoke(instr)
} else if g := call.StaticCallee(); g != nil {
r.addEdge(f, instr, g, false)
} else if _, ok := call.Value.(*ssa.Builtin); !ok {
r.visitDynCall(instr)
}
// Ignore the call-position operand when
// looking for address-taken Functions.
// Hack: assume this is rands[0].
rands = rands[1:]
case *ssa.MakeInterface:
// Converting a value of type T to an
// interface materializes its runtime
// type, allowing any of its exported
// methods to be called though reflection.
r.addRuntimeType(instr.X.Type(), false)
}
// Process all address-taken functions.
for _, op := range rands {
if g, ok := (*op).(*ssa.Function); ok {
r.visitAddrTakenFunc(g)
}
}
}
}
}
// Analyze performs Rapid Type Analysis, starting at the specified root
// functions. It returns nil if no roots were specified.
//
// The root functions must be one or more entrypoints (main and init
// functions) of a complete SSA program, with function bodies for all
// dependencies, constructed with the [ssa.InstantiateGenerics] mode
// flag.
//
// If buildCallGraph is true, Result.CallGraph will contain a call
// graph; otherwise, only the other fields (reachable functions) are
// populated.
func Analyze(roots []*ssa.Function, buildCallGraph bool) *Result {
if len(roots) == 0 {
return nil
}
r := &rta{
result: &Result{Reachable: make(map[*ssa.Function]struct{ AddrTaken bool })},
prog: roots[0].Prog,
}
if buildCallGraph {
// TODO(adonovan): change callgraph API to eliminate the
// notion of a distinguished root node. Some callgraphs
// have many roots, or none.
r.result.CallGraph = callgraph.New(roots[0])
}
// Grab ssa.Function for (*reflect.Value).Call,
// if "reflect" is among the dependencies.
if reflectPkg := r.prog.ImportedPackage("reflect"); reflectPkg != nil {
reflectValue := reflectPkg.Members["Value"].(*ssa.Type)
r.reflectValueCall = r.prog.LookupMethod(reflectValue.Object().Type(), reflectPkg.Pkg, "Call")
}
hasher := typeutil.MakeHasher()
r.result.RuntimeTypes.SetHasher(hasher)
r.addrTakenFuncsBySig.SetHasher(hasher)
r.dynCallSites.SetHasher(hasher)
r.invokeSites.SetHasher(hasher)
r.concreteTypes.SetHasher(hasher)
r.interfaceTypes.SetHasher(hasher)
for _, root := range roots {
r.addReachable(root, false)
}
// Visit functions, processing their instructions, and adding
// new functions to the worklist, until a fixed point is
// reached.
var shadow []*ssa.Function // for efficiency, we double-buffer the worklist
for len(r.worklist) > 0 {
shadow, r.worklist = r.worklist, shadow[:0]
for _, f := range shadow {
r.visitFunc(f)
}
}
return r.result
}
// interfaces(C) returns all currently known interfaces implemented by C.
func (r *rta) interfaces(C types.Type) []*types.Interface {
// Create an info for C the first time we see it.
var cinfo *concreteTypeInfo
if v := r.concreteTypes.At(C); v != nil {
cinfo = v.(*concreteTypeInfo)
} else {
mset := r.prog.MethodSets.MethodSet(C)
cinfo = &concreteTypeInfo{
C: C,
mset: mset,
fprint: fingerprint(mset),
}
r.concreteTypes.Set(C, cinfo)
// Ascertain set of interfaces C implements
// and update the 'implements' relation.
r.interfaceTypes.Iterate(func(I types.Type, v any) {
iinfo := v.(*interfaceTypeInfo)
if I := types.Unalias(I).(*types.Interface); implements(cinfo, iinfo) {
iinfo.implementations = append(iinfo.implementations, C)
cinfo.implements = append(cinfo.implements, I)
}
})
}
return cinfo.implements
}
// implementations(I) returns all currently known concrete types that implement I.
func (r *rta) implementations(I *types.Interface) []types.Type {
// Create an info for I the first time we see it.
var iinfo *interfaceTypeInfo
if v := r.interfaceTypes.At(I); v != nil {
iinfo = v.(*interfaceTypeInfo)
} else {
mset := r.prog.MethodSets.MethodSet(I)
iinfo = &interfaceTypeInfo{
I: I,
mset: mset,
fprint: fingerprint(mset),
}
r.interfaceTypes.Set(I, iinfo)
// Ascertain set of concrete types that implement I
// and update the 'implements' relation.
r.concreteTypes.Iterate(func(C types.Type, v any) {
cinfo := v.(*concreteTypeInfo)
if implements(cinfo, iinfo) {
cinfo.implements = append(cinfo.implements, I)
iinfo.implementations = append(iinfo.implementations, C)
}
})
}
return iinfo.implementations
}
// addRuntimeType is called for each concrete type that can be the
// dynamic type of some interface or reflect.Value.
// Adapted from needMethods in go/ssa/builder.go
func (r *rta) addRuntimeType(T types.Type, skip bool) {
// Never record aliases.
T = types.Unalias(T)
if prev, ok := r.result.RuntimeTypes.At(T).(bool); ok {
if skip && !prev {
r.result.RuntimeTypes.Set(T, skip)
}
return
}
r.result.RuntimeTypes.Set(T, skip)
mset := r.prog.MethodSets.MethodSet(T)
if _, ok := T.Underlying().(*types.Interface); !ok {
// T is a new concrete type.
for i, n := 0, mset.Len(); i < n; i++ {
sel := mset.At(i)
m := sel.Obj()
if m.Exported() {
// Exported methods are always potentially callable via reflection.
r.addReachable(r.prog.MethodValue(sel), true)
}
}
// Add callgraph edge for each existing dynamic
// "invoke"-mode call via that interface.
for _, I := range r.interfaces(T) {
sites, _ := r.invokeSites.At(I).([]ssa.CallInstruction)
for _, site := range sites {
r.addInvokeEdge(site, T)
}
}
}
// Precondition: T is not a method signature (*Signature with Recv()!=nil).
// Recursive case: skip => don't call makeMethods(T).
// Each package maintains its own set of types it has visited.
var n *types.Named
switch T := types.Unalias(T).(type) {
case *types.Named:
n = T
case *types.Pointer:
n, _ = types.Unalias(T.Elem()).(*types.Named)
}
if n != nil {
owner := n.Obj().Pkg()
if owner == nil {
return // built-in error type
}
}
// Recursion over signatures of each exported method.
for method := range mset.Methods() {
if method.Obj().Exported() {
sig := method.Type().(*types.Signature)
r.addRuntimeType(sig.Params(), true) // skip the Tuple itself
r.addRuntimeType(sig.Results(), true) // skip the Tuple itself
}
}
switch t := T.(type) {
case *types.Alias:
panic("unreachable")
case *types.Basic:
// nop
case *types.Interface:
// nop---handled by recursion over method set.
case *types.Pointer:
r.addRuntimeType(t.Elem(), false)
case *types.Slice:
r.addRuntimeType(t.Elem(), false)
case *types.Chan:
r.addRuntimeType(t.Elem(), false)
case *types.Map:
r.addRuntimeType(t.Key(), false)
r.addRuntimeType(t.Elem(), false)
case *types.Signature:
if t.Recv() != nil {
panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
}
r.addRuntimeType(t.Params(), true) // skip the Tuple itself
r.addRuntimeType(t.Results(), true) // skip the Tuple itself
case *types.Named:
// A pointer-to-named type can be derived from a named
// type via reflection. It may have methods too.
r.addRuntimeType(types.NewPointer(T), false)
// Consider 'type T struct{S}' where S has methods.
// Reflection provides no way to get from T to struct{S},
// only to S, so the method set of struct{S} is unwanted,
// so set 'skip' flag during recursion.
r.addRuntimeType(t.Underlying(), true)
case *types.Array:
r.addRuntimeType(t.Elem(), false)
case *types.Struct:
for i, n := 0, t.NumFields(); i < n; i++ {
r.addRuntimeType(t.Field(i).Type(), false)
}
case *types.Tuple:
for i, n := 0, t.Len(); i < n; i++ {
r.addRuntimeType(t.At(i).Type(), false)
}
default:
panic(T)
}
}
// fingerprint returns a bitmask with one bit set per method id,
// enabling 'implements' to quickly reject most candidates.
func fingerprint(mset *types.MethodSet) uint64 {
var space [64]byte
var mask uint64
for method := range mset.Methods() {
method := method.Obj()
sig := method.Type().(*types.Signature)
sum := crc32.ChecksumIEEE(fmt.Appendf(space[:], "%s/%d/%d",
method.Id(),
sig.Params().Len(),
sig.Results().Len()))
mask |= 1 << (sum % 64)
}
return mask
}
// implements reports whether types.Implements(cinfo.C, iinfo.I),
// but more efficiently.
func implements(cinfo *concreteTypeInfo, iinfo *interfaceTypeInfo) (got bool) {
// The concrete type must have at least the methods
// (bits) of the interface type. Use a bitwise subset
// test to reject most candidates quickly.
return iinfo.fprint & ^cinfo.fprint == 0 && types.Implements(cinfo.C, iinfo.I)
}

180
vendor/golang.org/x/tools/go/callgraph/util.go generated vendored Normal file
View File

@@ -0,0 +1,180 @@
// Copyright 2013 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 callgraph
import "golang.org/x/tools/go/ssa"
// This file provides various utilities over call graphs, such as
// visitation and path search.
// CalleesOf returns a new set containing all direct callees of the
// caller node.
func CalleesOf(caller *Node) map[*Node]bool {
callees := make(map[*Node]bool)
for _, e := range caller.Out {
callees[e.Callee] = true
}
return callees
}
// GraphVisitEdges visits all the edges in graph g in depth-first order.
// The edge function is called for each edge in postorder. If it
// returns non-nil, visitation stops and GraphVisitEdges returns that
// value.
func GraphVisitEdges(g *Graph, edge func(*Edge) error) error {
seen := make(map[*Node]bool)
var visit func(n *Node) error
visit = func(n *Node) error {
if !seen[n] {
seen[n] = true
for _, e := range n.Out {
if err := visit(e.Callee); err != nil {
return err
}
if err := edge(e); err != nil {
return err
}
}
}
return nil
}
for _, n := range g.Nodes {
if err := visit(n); err != nil {
return err
}
}
return nil
}
// PathSearch finds an arbitrary path starting at node start and
// ending at some node for which isEnd() returns true. On success,
// PathSearch returns the path as an ordered list of edges; on
// failure, it returns nil.
func PathSearch(start *Node, isEnd func(*Node) bool) []*Edge {
stack := make([]*Edge, 0, 32)
seen := make(map[*Node]bool)
var search func(n *Node) []*Edge
search = func(n *Node) []*Edge {
if !seen[n] {
seen[n] = true
if isEnd(n) {
return stack
}
for _, e := range n.Out {
stack = append(stack, e) // push
if found := search(e.Callee); found != nil {
return found
}
stack = stack[:len(stack)-1] // pop
}
}
return nil
}
return search(start)
}
// DeleteSyntheticNodes removes from call graph g all nodes for
// functions that do not correspond to source syntax. For historical
// reasons, nodes for g.Root and package initializers are always
// kept.
//
// As nodes are removed, edges are created to preserve the
// reachability relation of the remaining nodes.
func (g *Graph) DeleteSyntheticNodes() {
// Measurements on the standard library and go.tools show that
// resulting graph has ~15% fewer nodes and 4-8% fewer edges
// than the input.
//
// Inlining a wrapper of in-degree m, out-degree n adds m*n
// and removes m+n edges. Since most wrappers are monomorphic
// (n=1) this results in a slight reduction. Polymorphic
// wrappers (n>1), e.g. from embedding an interface value
// inside a struct to satisfy some interface, cause an
// increase in the graph, but they seem to be uncommon.
// Hash all existing edges to avoid creating duplicates.
edges := make(map[Edge]bool)
for _, cgn := range g.Nodes {
for _, e := range cgn.Out {
edges[*e] = true
}
}
for fn, cgn := range g.Nodes {
if cgn == g.Root || isInit(cgn.Func) || fn.Syntax() != nil {
continue // keep
}
for _, eIn := range cgn.In {
for _, eOut := range cgn.Out {
newEdge := Edge{eIn.Caller, eIn.Site, eOut.Callee}
if edges[newEdge] {
continue // don't add duplicate
}
AddEdge(eIn.Caller, eIn.Site, eOut.Callee)
edges[newEdge] = true
}
}
g.DeleteNode(cgn)
}
}
func isInit(fn *ssa.Function) bool {
return fn.Pkg != nil && fn.Pkg.Func("init") == fn
}
// DeleteNode removes node n and its edges from the graph g.
// (NB: not efficient for batch deletion.)
func (g *Graph) DeleteNode(n *Node) {
n.deleteIns()
n.deleteOuts()
delete(g.Nodes, n.Func)
}
// deleteIns deletes all incoming edges to n.
func (n *Node) deleteIns() {
for _, e := range n.In {
removeOutEdge(e)
}
n.In = nil
}
// deleteOuts deletes all outgoing edges from n.
func (n *Node) deleteOuts() {
for _, e := range n.Out {
removeInEdge(e)
}
n.Out = nil
}
// removeOutEdge removes edge.Caller's outgoing edge 'edge'.
func removeOutEdge(edge *Edge) {
caller := edge.Caller
n := len(caller.Out)
for i, e := range caller.Out {
if e == edge {
// Replace it with the final element and shrink the slice.
caller.Out[i] = caller.Out[n-1]
caller.Out[n-1] = nil // aid GC
caller.Out = caller.Out[:n-1]
return
}
}
panic("edge not found: " + edge.String())
}
// removeInEdge removes edge.Callee's incoming edge 'edge'.
func removeInEdge(edge *Edge) {
caller := edge.Callee
n := len(caller.In)
for i, e := range caller.In {
if e == edge {
// Replace it with the final element and shrink the slice.
caller.In[i] = caller.In[n-1]
caller.In[n-1] = nil // aid GC
caller.In = caller.In[:n-1]
return
}
}
panic("edge not found: " + edge.String())
}

861
vendor/golang.org/x/tools/go/callgraph/vta/graph.go generated vendored Normal file
View File

@@ -0,0 +1,861 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vta
import (
"fmt"
"go/token"
"go/types"
"iter"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typeparams"
)
// node interface for VTA nodes.
type node interface {
Type() types.Type
String() string
}
// constant node for VTA.
type constant struct {
typ types.Type
}
func (c constant) Type() types.Type {
return c.typ
}
func (c constant) String() string {
return fmt.Sprintf("Constant(%v)", c.Type())
}
// pointer node for VTA.
type pointer struct {
typ *types.Pointer
}
func (p pointer) Type() types.Type {
return p.typ
}
func (p pointer) String() string {
return fmt.Sprintf("Pointer(%v)", p.Type())
}
// mapKey node for VTA, modeling reachable map key types.
type mapKey struct {
typ types.Type
}
func (mk mapKey) Type() types.Type {
return mk.typ
}
func (mk mapKey) String() string {
return fmt.Sprintf("MapKey(%v)", mk.Type())
}
// mapValue node for VTA, modeling reachable map value types.
type mapValue struct {
typ types.Type
}
func (mv mapValue) Type() types.Type {
return mv.typ
}
func (mv mapValue) String() string {
return fmt.Sprintf("MapValue(%v)", mv.Type())
}
// sliceElem node for VTA, modeling reachable slice and array element types.
type sliceElem struct {
typ types.Type
}
func (s sliceElem) Type() types.Type {
return s.typ
}
func (s sliceElem) String() string {
return fmt.Sprintf("Slice([]%v)", s.Type())
}
// channelElem node for VTA, modeling reachable channel element types.
type channelElem struct {
typ types.Type
}
func (c channelElem) Type() types.Type {
return c.typ
}
func (c channelElem) String() string {
return fmt.Sprintf("Channel(chan %v)", c.Type())
}
// field node for VTA.
type field struct {
StructType types.Type
index int // index of the field in the struct
}
func (f field) Type() types.Type {
s := typeparams.CoreType(f.StructType).(*types.Struct)
return s.Field(f.index).Type()
}
func (f field) String() string {
s := typeparams.CoreType(f.StructType).(*types.Struct)
return fmt.Sprintf("Field(%v:%s)", f.StructType, s.Field(f.index).Name())
}
// global node for VTA.
type global struct {
val *ssa.Global
}
func (g global) Type() types.Type {
return g.val.Type()
}
func (g global) String() string {
return fmt.Sprintf("Global(%s)", g.val.Name())
}
// local node for VTA modeling local variables
// and function/method parameters.
type local struct {
val ssa.Value
}
func (l local) Type() types.Type {
return l.val.Type()
}
func (l local) String() string {
return fmt.Sprintf("Local(%s)", l.val.Name())
}
// indexedLocal node for VTA node. Models indexed locals
// related to the ssa extract instructions.
type indexedLocal struct {
val ssa.Value
index int
typ types.Type
}
func (i indexedLocal) Type() types.Type {
return i.typ
}
func (i indexedLocal) String() string {
return fmt.Sprintf("Local(%s[%d])", i.val.Name(), i.index)
}
// function node for VTA.
type function struct {
f *ssa.Function
}
func (f function) Type() types.Type {
return f.f.Type()
}
func (f function) String() string {
return fmt.Sprintf("Function(%s)", f.f.Name())
}
// resultVar represents the result
// variable of a function, whether
// named or not.
type resultVar struct {
f *ssa.Function
index int // valid index into result var tuple
}
func (o resultVar) Type() types.Type {
return o.f.Signature.Results().At(o.index).Type()
}
func (o resultVar) String() string {
v := o.f.Signature.Results().At(o.index)
if n := v.Name(); n != "" {
return fmt.Sprintf("Return(%s[%s])", o.f.Name(), n)
}
return fmt.Sprintf("Return(%s[%d])", o.f.Name(), o.index)
}
// nestedPtrInterface node represents all references and dereferences
// of locals and globals that have a nested pointer to interface type.
// We merge such constructs into a single node for simplicity and without
// much precision sacrifice as such variables are rare in practice. Both
// a and b would be represented as the same PtrInterface(I) node in:
//
// type I interface
// var a ***I
// var b **I
type nestedPtrInterface struct {
typ types.Type
}
func (l nestedPtrInterface) Type() types.Type {
return l.typ
}
func (l nestedPtrInterface) String() string {
return fmt.Sprintf("PtrInterface(%v)", l.typ)
}
// nestedPtrFunction node represents all references and dereferences of locals
// and globals that have a nested pointer to function type. We merge such
// constructs into a single node for simplicity and without much precision
// sacrifice as such variables are rare in practice. Both a and b would be
// represented as the same PtrFunction(func()) node in:
//
// var a *func()
// var b **func()
type nestedPtrFunction struct {
typ types.Type
}
func (p nestedPtrFunction) Type() types.Type {
return p.typ
}
func (p nestedPtrFunction) String() string {
return fmt.Sprintf("PtrFunction(%v)", p.typ)
}
// panicArg models types of all arguments passed to panic.
type panicArg struct{}
func (p panicArg) Type() types.Type {
return nil
}
func (p panicArg) String() string {
return "Panic"
}
// recoverReturn models types of all return values of recover().
type recoverReturn struct{}
func (r recoverReturn) Type() types.Type {
return nil
}
func (r recoverReturn) String() string {
return "Recover"
}
type empty = struct{}
// idx is an index representing a unique node in a vtaGraph.
type idx int
// vtaGraph remembers for each VTA node the set of its successors.
// Tailored for VTA, hence does not support singleton (sub)graphs.
type vtaGraph struct {
m []map[idx]empty // m[i] has the successors for the node with index i.
idx map[node]idx // idx[n] is the index for the node n.
node []node // node[i] is the node with index i.
}
func (g *vtaGraph) numNodes() int {
return len(g.idx)
}
func (g *vtaGraph) successors(x idx) iter.Seq[idx] {
return func(yield func(y idx) bool) {
for y := range g.m[x] {
if !yield(y) {
return
}
}
}
}
// addEdge adds an edge x->y to the graph.
func (g *vtaGraph) addEdge(x, y node) {
if g.idx == nil {
g.idx = make(map[node]idx)
}
lookup := func(n node) idx {
i, ok := g.idx[n]
if !ok {
i = idx(len(g.idx))
g.m = append(g.m, nil)
g.idx[n] = i
g.node = append(g.node, n)
}
return i
}
a := lookup(x)
b := lookup(y)
succs := g.m[a]
if succs == nil {
succs = make(map[idx]empty)
g.m[a] = succs
}
succs[b] = empty{}
}
// typePropGraph builds a VTA graph for a set of `funcs` and initial
// `callgraph` needed to establish interprocedural edges. Returns the
// graph and a map for unique type representatives.
func typePropGraph(funcs map[*ssa.Function]bool, callees calleesFunc) (*vtaGraph, *typeutil.Map) {
b := builder{callees: callees}
b.visit(funcs)
b.callees = nil // ensure callees is not pinned by pointers to other fields of b.
return &b.graph, &b.canon
}
// Data structure responsible for linearly traversing the
// code and building a VTA graph.
type builder struct {
graph vtaGraph
callees calleesFunc // initial call graph for creating flows at unresolved call sites.
// Specialized type map for canonicalization of types.Type.
// Semantically equivalent types can have different implementations,
// i.e., they are different pointer values. The map allows us to
// have one unique representative. The keys are fixed and from the
// client perspective they are types. The values in our case are
// types too, in particular type representatives. Each value is a
// pointer so this map is not expected to take much memory.
canon typeutil.Map
}
func (b *builder) visit(funcs map[*ssa.Function]bool) {
// Add the fixed edge Panic -> Recover
b.graph.addEdge(panicArg{}, recoverReturn{})
for f, in := range funcs {
if in {
b.fun(f)
}
}
}
func (b *builder) fun(f *ssa.Function) {
for _, bl := range f.Blocks {
for _, instr := range bl.Instrs {
b.instr(instr)
}
}
}
func (b *builder) instr(instr ssa.Instruction) {
switch i := instr.(type) {
case *ssa.Store:
b.addInFlowAliasEdges(b.nodeFromVal(i.Addr), b.nodeFromVal(i.Val))
case *ssa.MakeInterface:
b.addInFlowEdge(b.nodeFromVal(i.X), b.nodeFromVal(i))
case *ssa.MakeClosure:
b.closure(i)
case *ssa.UnOp:
b.unop(i)
case *ssa.Phi:
b.phi(i)
case *ssa.ChangeInterface:
// Although in change interface a := A(b) command a and b are
// the same object, the only interesting flow happens when A
// is an interface. We create flow b -> a, but omit a -> b.
// The latter flow is not needed: if a gets assigned concrete
// type later on, that cannot be propagated back to b as b
// is a separate variable. The a -> b flow can happen when
// A is a pointer to interface, but then the command is of
// type ChangeType, handled below.
b.addInFlowEdge(b.nodeFromVal(i.X), b.nodeFromVal(i))
case *ssa.ChangeType:
// change type command a := A(b) results in a and b being the
// same value. For concrete type A, there is no interesting flow.
//
// When A is an interface, most interface casts are handled
// by the ChangeInterface instruction. The relevant case here is
// when converting a pointer to an interface type. This can happen
// when the underlying interfaces have the same method set.
//
// type I interface{ foo() }
// type J interface{ foo() }
// var b *I
// a := (*J)(b)
//
// When this happens we add flows between a <--> b.
b.addInFlowAliasEdges(b.nodeFromVal(i), b.nodeFromVal(i.X))
case *ssa.TypeAssert:
b.tassert(i)
case *ssa.Extract:
b.extract(i)
case *ssa.Field:
b.field(i)
case *ssa.FieldAddr:
b.fieldAddr(i)
case *ssa.Send:
b.send(i)
case *ssa.Select:
b.selekt(i)
case *ssa.Index:
b.index(i)
case *ssa.IndexAddr:
b.indexAddr(i)
case *ssa.Lookup:
b.lookup(i)
case *ssa.MapUpdate:
b.mapUpdate(i)
case *ssa.Next:
b.next(i)
case ssa.CallInstruction:
b.call(i)
case *ssa.Panic:
b.panic(i)
case *ssa.Return:
b.rtrn(i)
case *ssa.MakeChan, *ssa.MakeMap, *ssa.MakeSlice, *ssa.BinOp,
*ssa.Alloc, *ssa.DebugRef, *ssa.Convert, *ssa.Jump, *ssa.If,
*ssa.Slice, *ssa.SliceToArrayPointer, *ssa.Range, *ssa.RunDefers:
// No interesting flow here.
// Notes on individual instructions:
// SliceToArrayPointer: t1 = slice to array pointer *[4]T <- []T (t0)
// No interesting flow as sliceArrayElem(t1) == sliceArrayElem(t0).
return
case *ssa.MultiConvert:
b.multiconvert(i)
default:
panic(fmt.Sprintf("unsupported instruction %v\n", instr))
}
}
func (b *builder) unop(u *ssa.UnOp) {
switch u.Op {
case token.MUL:
// Multiplication operator * is used here as a dereference operator.
b.addInFlowAliasEdges(b.nodeFromVal(u), b.nodeFromVal(u.X))
case token.ARROW:
t := typeparams.CoreType(u.X.Type()).(*types.Chan).Elem()
b.addInFlowAliasEdges(b.nodeFromVal(u), channelElem{typ: t})
default:
// There is no interesting type flow otherwise.
}
}
func (b *builder) phi(p *ssa.Phi) {
for _, edge := range p.Edges {
b.addInFlowAliasEdges(b.nodeFromVal(p), b.nodeFromVal(edge))
}
}
func (b *builder) tassert(a *ssa.TypeAssert) {
if !a.CommaOk {
b.addInFlowEdge(b.nodeFromVal(a.X), b.nodeFromVal(a))
return
}
// The case where a is <a.AssertedType, bool> register so there
// is a flow from a.X to a[0]. Here, a[0] is represented as an
// indexedLocal: an entry into local tuple register a at index 0.
tup := a.Type().(*types.Tuple)
t := tup.At(0).Type()
local := indexedLocal{val: a, typ: t, index: 0}
b.addInFlowEdge(b.nodeFromVal(a.X), local)
}
// extract instruction t1 := t2[i] generates flows between t2[i]
// and t1 where the source is indexed local representing a value
// from tuple register t2 at index i and the target is t1.
func (b *builder) extract(e *ssa.Extract) {
tup := e.Tuple.Type().(*types.Tuple)
t := tup.At(e.Index).Type()
local := indexedLocal{val: e.Tuple, typ: t, index: e.Index}
b.addInFlowAliasEdges(b.nodeFromVal(e), local)
}
func (b *builder) field(f *ssa.Field) {
fnode := field{StructType: f.X.Type(), index: f.Field}
b.addInFlowEdge(fnode, b.nodeFromVal(f))
}
func (b *builder) fieldAddr(f *ssa.FieldAddr) {
t := typeparams.CoreType(f.X.Type()).(*types.Pointer).Elem()
// Since we are getting pointer to a field, make a bidirectional edge.
fnode := field{StructType: t, index: f.Field}
b.addInFlowEdge(fnode, b.nodeFromVal(f))
b.addInFlowEdge(b.nodeFromVal(f), fnode)
}
func (b *builder) send(s *ssa.Send) {
t := typeparams.CoreType(s.Chan.Type()).(*types.Chan).Elem()
b.addInFlowAliasEdges(channelElem{typ: t}, b.nodeFromVal(s.X))
}
// selekt generates flows for select statement
//
// a = select blocking/nonblocking [c_1 <- t_1, c_2 <- t_2, ..., <- o_1, <- o_2, ...]
//
// between receiving channel registers c_i and corresponding input register t_i. Further,
// flows are generated between o_i and a[2 + i]. Note that a is a tuple register of type
// <int, bool, r_1, r_2, ...> where the type of r_i is the element type of channel o_i.
func (b *builder) selekt(s *ssa.Select) {
recvIndex := 0
for _, state := range s.States {
t := typeparams.CoreType(state.Chan.Type()).(*types.Chan).Elem()
if state.Dir == types.SendOnly {
b.addInFlowAliasEdges(channelElem{typ: t}, b.nodeFromVal(state.Send))
} else {
// state.Dir == RecvOnly by definition of select instructions.
tupEntry := indexedLocal{val: s, typ: t, index: 2 + recvIndex}
b.addInFlowAliasEdges(tupEntry, channelElem{typ: t})
recvIndex++
}
}
}
// index instruction a := b[c] on slices creates flows between a and
// SliceElem(t) flow where t is an interface type of c. Arrays and
// slice elements are both modeled as SliceElem.
func (b *builder) index(i *ssa.Index) {
et := sliceArrayElem(i.X.Type())
b.addInFlowAliasEdges(b.nodeFromVal(i), sliceElem{typ: et})
}
// indexAddr instruction a := &b[c] fetches address of a index
// into the field so we create bidirectional flow a <-> SliceElem(t)
// where t is an interface type of c. Arrays and slice elements are
// both modeled as SliceElem.
func (b *builder) indexAddr(i *ssa.IndexAddr) {
et := sliceArrayElem(i.X.Type())
b.addInFlowEdge(sliceElem{typ: et}, b.nodeFromVal(i))
b.addInFlowEdge(b.nodeFromVal(i), sliceElem{typ: et})
}
// lookup handles map query commands a := m[b] where m is of type
// map[...]V and V is an interface. It creates flows between `a`
// and MapValue(V).
func (b *builder) lookup(l *ssa.Lookup) {
t, ok := l.X.Type().Underlying().(*types.Map)
if !ok {
// No interesting flows for string lookups.
return
}
if !l.CommaOk {
b.addInFlowAliasEdges(b.nodeFromVal(l), mapValue{typ: t.Elem()})
} else {
i := indexedLocal{val: l, typ: t.Elem(), index: 0}
b.addInFlowAliasEdges(i, mapValue{typ: t.Elem()})
}
}
// mapUpdate handles map update commands m[b] = a where m is of type
// map[K]V and K and V are interfaces. It creates flows between `a`
// and MapValue(V) as well as between MapKey(K) and `b`.
func (b *builder) mapUpdate(u *ssa.MapUpdate) {
t, ok := u.Map.Type().Underlying().(*types.Map)
if !ok {
// No interesting flows for string updates.
return
}
b.addInFlowAliasEdges(mapKey{typ: t.Key()}, b.nodeFromVal(u.Key))
b.addInFlowAliasEdges(mapValue{typ: t.Elem()}, b.nodeFromVal(u.Value))
}
// next instruction <ok, key, value> := next r, where r
// is a range over map or string generates flow between
// key and MapKey as well value and MapValue nodes.
func (b *builder) next(n *ssa.Next) {
if n.IsString {
return
}
tup := n.Type().(*types.Tuple)
kt := tup.At(1).Type()
vt := tup.At(2).Type()
b.addInFlowAliasEdges(indexedLocal{val: n, typ: kt, index: 1}, mapKey{typ: kt})
b.addInFlowAliasEdges(indexedLocal{val: n, typ: vt, index: 2}, mapValue{typ: vt})
}
// addInFlowAliasEdges adds an edge r -> l to b.graph if l is a node that can
// have an inflow, i.e., a node that represents an interface or an unresolved
// function value. Similarly for the edge l -> r with an additional condition
// of that l and r can potentially alias.
func (b *builder) addInFlowAliasEdges(l, r node) {
b.addInFlowEdge(r, l)
if canAlias(l, r) {
b.addInFlowEdge(l, r)
}
}
func (b *builder) closure(c *ssa.MakeClosure) {
f := c.Fn.(*ssa.Function)
b.addInFlowEdge(function{f: f}, b.nodeFromVal(c))
for i, fv := range f.FreeVars {
b.addInFlowAliasEdges(b.nodeFromVal(fv), b.nodeFromVal(c.Bindings[i]))
}
}
// panic creates a flow from arguments to panic instructions to return
// registers of all recover statements in the program. Introduces a
// global panic node Panic and
// 1. for every panic statement p: add p -> Panic
// 2. for every recover statement r: add Panic -> r (handled in call)
//
// TODO(zpavlinovic): improve precision by explicitly modeling how panic
// values flow from callees to callers and into deferred recover instructions.
func (b *builder) panic(p *ssa.Panic) {
// Panics often have, for instance, strings as arguments which do
// not create interesting flows.
if !canHaveMethods(p.X.Type()) {
return
}
b.addInFlowEdge(b.nodeFromVal(p.X), panicArg{})
}
// call adds flows between arguments/parameters and return values/registers
// for both static and dynamic calls, as well as go and defer calls.
func (b *builder) call(c ssa.CallInstruction) {
// When c is r := recover() call register instruction, we add Recover -> r.
if bf, ok := c.Common().Value.(*ssa.Builtin); ok && bf.Name() == "recover" {
if v, ok := c.(ssa.Value); ok {
b.addInFlowEdge(recoverReturn{}, b.nodeFromVal(v))
}
return
}
for f := range siteCallees(c, b.callees) {
addArgumentFlows(b, c, f)
site, ok := c.(ssa.Value)
if !ok {
continue // go or defer
}
results := f.Signature.Results()
if results.Len() == 1 {
// When there is only one return value, the destination register does not
// have a tuple type.
b.addInFlowEdge(resultVar{f: f, index: 0}, b.nodeFromVal(site))
} else {
tup := site.Type().(*types.Tuple)
for i := 0; i < results.Len(); i++ {
local := indexedLocal{val: site, typ: tup.At(i).Type(), index: i}
b.addInFlowEdge(resultVar{f: f, index: i}, local)
}
}
}
}
func addArgumentFlows(b *builder, c ssa.CallInstruction, f *ssa.Function) {
// When f has no parameters (including receiver), there is no type
// flow here. Also, f's body and parameters might be missing, such
// as when vta is used within the golang.org/x/tools/go/analysis
// framework (see github.com/golang/go/issues/50670).
if len(f.Params) == 0 {
return
}
cc := c.Common()
if cc.Method != nil {
// In principle we don't add interprocedural flows for receiver
// objects. At a call site, the receiver object is interface
// while the callee object is concrete. The flow from interface
// to concrete type in general does not make sense. The exception
// is when the concrete type is a named function type (see #57756).
//
// The flow other way around would bake in information from the
// initial call graph.
if isFunction(f.Params[0].Type()) {
b.addInFlowEdge(b.nodeFromVal(cc.Value), b.nodeFromVal(f.Params[0]))
}
}
offset := 0
if cc.Method != nil {
offset = 1
}
for i, v := range cc.Args {
// Parameters of f might not be available, as in the case
// when vta is used within the golang.org/x/tools/go/analysis
// framework (see github.com/golang/go/issues/50670).
//
// TODO: investigate other cases of missing body and parameters
if len(f.Params) <= i+offset {
return
}
b.addInFlowAliasEdges(b.nodeFromVal(f.Params[i+offset]), b.nodeFromVal(v))
}
}
// rtrn creates flow edges from the operands of the return
// statement to the result variables of the enclosing function.
func (b *builder) rtrn(r *ssa.Return) {
for i, rs := range r.Results {
b.addInFlowEdge(b.nodeFromVal(rs), resultVar{f: r.Parent(), index: i})
}
}
func (b *builder) multiconvert(c *ssa.MultiConvert) {
// TODO(zpavlinovic): decide what to do on MultiConvert long term.
// TODO(zpavlinovic): add unit tests.
typeSetOf := func(typ types.Type) []*types.Term {
// This is a adaptation of x/exp/typeparams.NormalTerms which x/tools cannot depend on.
var terms []*types.Term
var err error
switch typ := types.Unalias(typ).(type) {
case *types.TypeParam:
terms, err = typeparams.StructuralTerms(typ)
case *types.Union:
terms, err = typeparams.UnionTermSet(typ)
case *types.Interface:
terms, err = typeparams.InterfaceTermSet(typ)
default:
// Common case.
// Specializing the len=1 case to avoid a slice
// had no measurable space/time benefit.
terms = []*types.Term{types.NewTerm(false, typ)}
}
if err != nil {
return nil
}
return terms
}
// isValuePreserving returns true if a conversion from ut_src to
// ut_dst is value-preserving, i.e. just a change of type.
// Precondition: neither argument is a named or alias type.
isValuePreserving := func(ut_src, ut_dst types.Type) bool {
// Identical underlying types?
if types.IdenticalIgnoreTags(ut_dst, ut_src) {
return true
}
switch ut_dst.(type) {
case *types.Chan:
// Conversion between channel types?
_, ok := ut_src.(*types.Chan)
return ok
case *types.Pointer:
// Conversion between pointers with identical base types?
_, ok := ut_src.(*types.Pointer)
return ok
}
return false
}
dst_terms := typeSetOf(c.Type())
src_terms := typeSetOf(c.X.Type())
for _, s := range src_terms {
us := s.Type().Underlying()
for _, d := range dst_terms {
ud := d.Type().Underlying()
if isValuePreserving(us, ud) {
// This is equivalent to a ChangeType.
b.addInFlowAliasEdges(b.nodeFromVal(c), b.nodeFromVal(c.X))
return
}
// This is equivalent to either: SliceToArrayPointer,,
// SliceToArrayPointer+Deref, Size 0 Array constant, or a Convert.
}
}
}
// addInFlowEdge adds s -> d to g if d is node that can have an inflow, i.e., a node
// that represents an interface or an unresolved function value. Otherwise, there
// is no interesting type flow so the edge is omitted.
func (b *builder) addInFlowEdge(s, d node) {
if hasInFlow(d) {
b.graph.addEdge(b.representative(s), b.representative(d))
}
}
// Creates const, pointer, global, func, and local nodes based on register instructions.
func (b *builder) nodeFromVal(val ssa.Value) node {
if p, ok := types.Unalias(val.Type()).(*types.Pointer); ok && !types.IsInterface(p.Elem()) && !isFunction(p.Elem()) {
// Nested pointer to interfaces are modeled as a special
// nestedPtrInterface node.
if i := interfaceUnderPtr(p.Elem()); i != nil {
return nestedPtrInterface{typ: i}
}
// The same goes for nested function types.
if f := functionUnderPtr(p.Elem()); f != nil {
return nestedPtrFunction{typ: f}
}
return pointer{typ: p}
}
switch v := val.(type) {
case *ssa.Const:
return constant{typ: val.Type()}
case *ssa.Global:
return global{val: v}
case *ssa.Function:
return function{f: v}
case *ssa.Parameter, *ssa.FreeVar, ssa.Instruction:
// ssa.Param, ssa.FreeVar, and a specific set of "register" instructions,
// satisfying the ssa.Value interface, can serve as local variables.
return local{val: v}
default:
panic(fmt.Errorf("unsupported value %v in node creation", val))
}
}
// representative returns a unique representative for node `n`. Since
// semantically equivalent types can have different implementations,
// this method guarantees the same implementation is always used.
func (b *builder) representative(n node) node {
if n.Type() == nil {
// panicArg and recoverReturn do not have
// types and are unique by definition.
return n
}
t := canonicalize(n.Type(), &b.canon)
switch i := n.(type) {
case constant:
return constant{typ: t}
case pointer:
return pointer{typ: t.(*types.Pointer)}
case sliceElem:
return sliceElem{typ: t}
case mapKey:
return mapKey{typ: t}
case mapValue:
return mapValue{typ: t}
case channelElem:
return channelElem{typ: t}
case nestedPtrInterface:
return nestedPtrInterface{typ: t}
case nestedPtrFunction:
return nestedPtrFunction{typ: t}
case field:
return field{StructType: canonicalize(i.StructType, &b.canon), index: i.index}
case indexedLocal:
return indexedLocal{typ: t, val: i.val, index: i.index}
case local, global, panicArg, recoverReturn, function, resultVar:
return n
default:
panic(fmt.Errorf("canonicalizing unrecognized node %v", n))
}
}
// canonicalize returns a type representative of `t` unique subject
// to type map `canon`.
func canonicalize(t types.Type, canon *typeutil.Map) types.Type {
rep := canon.At(t)
if rep != nil {
return rep.(types.Type)
}
canon.Set(t, t)
return t
}

37
vendor/golang.org/x/tools/go/callgraph/vta/initial.go generated vendored Normal file
View File

@@ -0,0 +1,37 @@
// 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.
package vta
import (
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/internal/chautil"
"golang.org/x/tools/go/ssa"
)
// calleesFunc abstracts call graph in one direction,
// from call sites to callees.
type calleesFunc func(ssa.CallInstruction) []*ssa.Function
// makeCalleesFunc returns an initial call graph for vta as a
// calleesFunc. If c is not nil, returns callees as given by c.
// Otherwise, it returns chautil.LazyCallees over fs.
func makeCalleesFunc(fs map[*ssa.Function]bool, c *callgraph.Graph) calleesFunc {
if c == nil {
return chautil.LazyCallees(fs)
}
return func(call ssa.CallInstruction) []*ssa.Function {
node := c.Nodes[call.Parent()]
if node == nil {
return nil
}
var cs []*ssa.Function
for _, edge := range node.Out {
if edge.Site == call {
cs = append(cs, edge.Callee.Func)
}
}
return cs
}
}

View File

@@ -0,0 +1,127 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package trie
import (
"math/bits"
)
// This file contains bit twiddling functions for Patricia tries.
// Consult this paper for details.
// C. Okasaki and A. Gill, “Fast mergeable integer maps,” in ACM SIGPLAN
// Workshop on ML, September 1998, pp. 7786.
// key is a key in a Map.
type key uint64
// bitpos is the position of a bit. A position is represented by having a 1
// bit in that position.
// Examples:
// - 0b0010 is the position of the `1` bit in 2.
// It is the 3rd most specific bit position in big endian encoding
// (0b0 and 0b1 are more specific).
// - 0b0100 is the position of the bit that 1 and 5 disagree on.
// - 0b0 is a special value indicating that all bit agree.
type bitpos uint64
// prefixes represent a set of keys that all agree with the
// prefix up to a bitpos m.
//
// The value for a prefix is determined by the mask(k, m) function.
// (See mask for details on the values.)
// A `p` prefix for position `m` matches a key `k` iff mask(k, m) == p.
// A prefix always mask(p, m) == p.
//
// A key is its own prefix for the bit position 64,
// e.g. seeing a `prefix(key)` is not a problem.
//
// Prefixes should never be turned into keys.
type prefix uint64
// branchingBit returns the position of the first bit in `x` and `y`
// that are not equal.
func branchingBit(x, y prefix) bitpos {
p := x ^ y
if p == 0 {
return 0
}
return bitpos(1) << uint(bits.Len64(uint64(p))-1) // uint conversion needed for go1.12
}
// zeroBit returns true if k has a 0 bit at position `b`.
func zeroBit(k prefix, b bitpos) bool {
return (uint64(k) & uint64(b)) == 0
}
// matchPrefix returns true if a prefix k matches a prefix p up to position `b`.
func matchPrefix(k prefix, p prefix, b bitpos) bool {
return mask(k, b) == p
}
// mask returns a prefix of `k` with all bits after and including `b` zeroed out.
//
// In big endian encoding, this value is the [64-(m-1)] most significant bits of k
// followed by a `0` bit at bitpos m, followed m-1 `1` bits.
// Examples:
//
// prefix(0b1011) for a bitpos 0b0100 represents the keys:
// 0b1000, 0b1001, 0b1010, 0b1011, 0b1100, 0b1101, 0b1110, 0b1111
//
// This mask function has the property that if matchPrefix(k, p, b), then
// k <= p if and only if zeroBit(k, m). This induces binary search tree tries.
// See Okasaki & Gill for more details about this choice of mask function.
//
// mask is idempotent for a given `b`, i.e. mask(mask(p, b), b) == mask(p,b).
func mask(k prefix, b bitpos) prefix {
return prefix((uint64(k) | (uint64(b) - 1)) & (^uint64(b)))
}
// ord returns true if m comes before n in the bit ordering.
func ord(m, n bitpos) bool {
return m > n // big endian encoding
}
// prefixesOverlap returns true if there is some key a prefix `p` for bitpos `m`
// can hold that can also be held by a prefix `q` for some bitpos `n`.
//
// This is equivalent to:
//
// m ==n && p == q,
// higher(m, n) && matchPrefix(q, p, m), or
// higher(n, m) && matchPrefix(p, q, n)
func prefixesOverlap(p prefix, m bitpos, q prefix, n bitpos) bool {
fbb := n
if ord(m, n) {
fbb = m
}
return mask(p, fbb) == mask(q, fbb)
// Lemma:
// mask(p, fbb) == mask(q, fbb)
// iff
// m > n && matchPrefix(q, p, m) or (note: big endian encoding)
// m < n && matchPrefix(p, q, n) or (note: big endian encoding)
// m ==n && p == q
// Quick-n-dirty proof:
// p == mask(p0, m) for some p0 by precondition.
// q == mask(q0, n) for some q0 by precondition.
// So mask(p, m) == p and mask(q, n) == q as mask(*, n') is idempotent.
//
// [=> proof]
// Suppose mask(p, fbb) == mask(q, fbb).
// if m ==n, p == mask(p, m) == mask(p, fbb) == mask(q, fbb) == mask(q, n) == q
// if m > n, fbb = firstBranchBit(m, n) = m (big endian).
// p == mask(p, m) == mask(p, fbb) == mask(q, fbb) == mask(q, m)
// so mask(q, m) == p or matchPrefix(q, p, m)
// if m < n, is symmetric to the above.
//
// [<= proof]
// case m ==n && p == q. Then mask(p, fbb) == mask(q, fbb)
//
// case m > n && matchPrefix(q, p, m).
// fbb == firstBranchBit(m, n) == m (by m>n).
// mask(q, fbb) == mask(q, m) == p == mask(p, m) == mask(p, fbb)
//
// case m < n && matchPrefix(p, q, n) is symmetric.
}

View File

@@ -0,0 +1,516 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package trie
// Collision functions combine a left and right hand side (lhs and rhs) values
// the two values are associated with the same key and produces the value that
// will be stored for the key.
//
// Collision functions must be idempotent:
//
// collision(x, x) == x for all x.
//
// Collisions functions may be applied whenever a value is inserted
// or two maps are merged, or intersected.
type Collision func(lhs any, rhs any) any
// TakeLhs always returns the left value in a collision.
func TakeLhs(lhs, rhs any) any { return lhs }
// TakeRhs always returns the right hand side in a collision.
func TakeRhs(lhs, rhs any) any { return rhs }
// Builder creates new Map. Each Builder has a unique Scope.
//
// IMPORTANT: Nodes are hash-consed internally to reduce memory consumption. To
// support hash-consing Builders keep an internal Map of all of the Maps that they
// have created. To GC any of the Maps created by the Builder, all references to
// the Builder must be dropped. This includes MutMaps.
type Builder struct {
scope Scope
// hash-consing maps for each node type.
empty *empty
leaves map[leaf]*leaf
branches map[branch]*branch
// It may be possible to support more types of patricia tries
// (e.g. non-hash-consed) by making Builder an interface and abstracting
// the mkLeaf and mkBranch functions.
}
// NewBuilder creates a new Builder with a unique Scope.
func NewBuilder() *Builder {
s := newScope()
return &Builder{
scope: s,
empty: &empty{s},
leaves: make(map[leaf]*leaf),
branches: make(map[branch]*branch),
}
}
func (b *Builder) Scope() Scope { return b.scope }
// Rescope changes the builder's scope to a new unique Scope.
//
// Any Maps created using the previous scope need to be Cloned
// before any operation.
//
// This makes the old internals of the Builder eligible to be GC'ed.
func (b *Builder) Rescope() {
s := newScope()
b.scope = s
b.empty = &empty{s}
b.leaves = make(map[leaf]*leaf)
b.branches = make(map[branch]*branch)
}
// Empty is the empty map.
func (b *Builder) Empty() Map { return Map{b.Scope(), b.empty} }
// InsertWith inserts a new association from k to v into the Map m to create a new map
// in the current scope and handle collisions using the collision function c.
//
// This is roughly corresponds to updating a map[uint64]interface{} by:
//
// if _, ok := m[k]; ok { m[k] = c(m[k], v} else { m[k] = v}
//
// An insertion or update happened whenever Insert(m, ...) != m .
func (b *Builder) InsertWith(c Collision, m Map, k uint64, v any) Map {
m = b.Clone(m)
return Map{b.Scope(), b.insert(c, m.n, b.mkLeaf(key(k), v), false)}
}
// Inserts a new association from key to value into the Map m to create
// a new map in the current scope.
//
// If there was a previous value mapped by key, keep the previously mapped value.
// This is roughly corresponds to updating a map[uint64]interface{} by:
//
// if _, ok := m[k]; ok { m[k] = val }
//
// This is equivalent to b.Merge(m, b.Create({k: v})).
func (b *Builder) Insert(m Map, k uint64, v any) Map {
return b.InsertWith(TakeLhs, m, k, v)
}
// Updates a (key, value) in the map. This is roughly corresponds to
// updating a map[uint64]interface{} by:
//
// m[key] = val
func (b *Builder) Update(m Map, key uint64, val any) Map {
return b.InsertWith(TakeRhs, m, key, val)
}
// Merge two maps lhs and rhs to create a new map in the current scope.
//
// Whenever there is a key in both maps (a collision), the resulting value mapped by
// the key will be `c(lhs[key], rhs[key])`.
func (b *Builder) MergeWith(c Collision, lhs, rhs Map) Map {
lhs, rhs = b.Clone(lhs), b.Clone(rhs)
return Map{b.Scope(), b.merge(c, lhs.n, rhs.n)}
}
// Merge two maps lhs and rhs to create a new map in the current scope.
//
// Whenever there is a key in both maps (a collision), the resulting value mapped by
// the key will be the value in lhs `b.Collision(lhs[key], rhs[key])`.
func (b *Builder) Merge(lhs, rhs Map) Map {
return b.MergeWith(TakeLhs, lhs, rhs)
}
// Clone returns a Map that contains the same (key, value) elements
// within b.Scope(), i.e. return m if m.Scope() == b.Scope() or return
// a deep copy of m within b.Scope() otherwise.
func (b *Builder) Clone(m Map) Map {
if m.Scope() == b.Scope() {
return m
} else if m.n == nil {
return Map{b.Scope(), b.empty}
}
return Map{b.Scope(), b.clone(m.n)}
}
func (b *Builder) clone(n node) node {
switch n := n.(type) {
case *empty:
return b.empty
case *leaf:
return b.mkLeaf(n.k, n.v)
case *branch:
return b.mkBranch(n.prefix, n.branching, b.clone(n.left), b.clone(n.right))
default:
panic("unreachable")
}
}
// Remove a key from a Map m and return the resulting Map.
func (b *Builder) Remove(m Map, k uint64) Map {
m = b.Clone(m)
return Map{b.Scope(), b.remove(m.n, key(k))}
}
// Intersect Maps lhs and rhs and returns a map with all of the keys in
// both lhs and rhs and the value comes from lhs, i.e.
//
// {(k, lhs[k]) | k in lhs, k in rhs}.
func (b *Builder) Intersect(lhs, rhs Map) Map {
return b.IntersectWith(TakeLhs, lhs, rhs)
}
// IntersectWith take lhs and rhs and returns the intersection
// with the value coming from the collision function, i.e.
//
// {(k, c(lhs[k], rhs[k]) ) | k in lhs, k in rhs}.
//
// The elements of the resulting map are always { <k, c(lhs[k], rhs[k]) > }
// for each key k that a key in both lhs and rhs.
func (b *Builder) IntersectWith(c Collision, lhs, rhs Map) Map {
l, r := b.Clone(lhs), b.Clone(rhs)
return Map{b.Scope(), b.intersect(c, l.n, r.n)}
}
// MutMap is a convenient wrapper for a Map and a *Builder that will be used to create
// new Maps from it.
type MutMap struct {
B *Builder
M Map
}
// MutEmpty is an empty MutMap for a builder.
func (b *Builder) MutEmpty() MutMap {
return MutMap{b, b.Empty()}
}
// Insert an element into the map using the collision function for the builder.
// Returns true if the element was inserted.
func (mm *MutMap) Insert(k uint64, v any) bool {
old := mm.M
mm.M = mm.B.Insert(old, k, v)
return old != mm.M
}
// Updates an element in the map. Returns true if the map was updated.
func (mm *MutMap) Update(k uint64, v any) bool {
old := mm.M
mm.M = mm.B.Update(old, k, v)
return old != mm.M
}
// Removes a key from the map. Returns true if the element was removed.
func (mm *MutMap) Remove(k uint64) bool {
old := mm.M
mm.M = mm.B.Remove(old, k)
return old != mm.M
}
// Merge another map into the current one using the collision function
// for the builder. Returns true if the map changed.
func (mm *MutMap) Merge(other Map) bool {
old := mm.M
mm.M = mm.B.Merge(old, other)
return old != mm.M
}
// Intersect another map into the current one using the collision function
// for the builder. Returns true if the map changed.
func (mm *MutMap) Intersect(other Map) bool {
old := mm.M
mm.M = mm.B.Intersect(old, other)
return old != mm.M
}
func (b *Builder) Create(m map[uint64]any) Map {
var leaves []*leaf
for k, v := range m {
leaves = append(leaves, b.mkLeaf(key(k), v))
}
return Map{b.Scope(), b.create(leaves)}
}
// Merge another map into the current one using the collision function
// for the builder. Returns true if the map changed.
func (mm *MutMap) MergeWith(c Collision, other Map) bool {
old := mm.M
mm.M = mm.B.MergeWith(c, old, other)
return old != mm.M
}
// creates a map for a collection of leaf nodes.
func (b *Builder) create(leaves []*leaf) node {
n := len(leaves)
if n == 0 {
return b.empty
} else if n == 1 {
return leaves[0]
}
// Note: we can do a more sophisticated algorithm by:
// - sorting the leaves ahead of time,
// - taking the prefix and branching bit of the min and max key,
// - binary searching for the branching bit,
// - splitting exactly where the branch will be, and
// - making the branch node for this prefix + branching bit.
// Skipping until this is a performance bottleneck.
m := n / 2 // (n >= 2) ==> 1 <= m < n
l, r := leaves[:m], leaves[m:]
return b.merge(nil, b.create(l), b.create(r))
}
// mkLeaf returns the hash-consed representative of (k, v) in the current scope.
func (b *Builder) mkLeaf(k key, v any) *leaf {
rep, ok := b.leaves[leaf{k, v}]
if !ok {
rep = &leaf{k, v} // heap-allocated copy
b.leaves[leaf{k, v}] = rep
}
return rep
}
// mkBranch returns the hash-consed representative of the tuple
//
// (prefix, branch, left, right)
//
// in the current scope.
func (b *Builder) mkBranch(p prefix, bp bitpos, left node, right node) *branch {
br := branch{
sz: left.size() + right.size(),
prefix: p,
branching: bp,
left: left,
right: right,
}
rep, ok := b.branches[br]
if !ok {
rep = new(branch) // heap-allocated copy
*rep = br
b.branches[br] = rep
}
return rep
}
// join two maps with prefixes p0 and p1 that are *known* to disagree.
func (b *Builder) join(p0 prefix, t0 node, p1 prefix, t1 node) *branch {
m := branchingBit(p0, p1)
var left, right node
if zeroBit(p0, m) {
left, right = t0, t1
} else {
left, right = t1, t0
}
prefix := mask(p0, m)
return b.mkBranch(prefix, m, left, right)
}
// collide two leaves with the same key to create a leaf
// with the collided value.
func (b *Builder) collide(c Collision, left, right *leaf) *leaf {
if left == right {
return left // c is idempotent: c(x, x) == x
}
val := left.v // keep the left value by default if c is nil
if c != nil {
val = c(left.v, right.v)
}
switch val {
case left.v:
return left
case right.v:
return right
default:
return b.mkLeaf(left.k, val)
}
}
// inserts a leaf l into a map m and returns the resulting map.
// When lhs is true, l is the left hand side in a collision.
// Both l and m are in the current scope.
func (b *Builder) insert(c Collision, m node, l *leaf, lhs bool) node {
switch m := m.(type) {
case *empty:
return l
case *leaf:
if m.k == l.k {
left, right := l, m
if !lhs {
left, right = right, left
}
return b.collide(c, left, right)
}
return b.join(prefix(l.k), l, prefix(m.k), m)
case *branch:
// fallthrough
}
// m is a branch
br := m.(*branch)
if !matchPrefix(prefix(l.k), br.prefix, br.branching) {
return b.join(prefix(l.k), l, br.prefix, br)
}
var left, right node
if zeroBit(prefix(l.k), br.branching) {
left, right = b.insert(c, br.left, l, lhs), br.right
} else {
left, right = br.left, b.insert(c, br.right, l, lhs)
}
if left == br.left && right == br.right {
return m
}
return b.mkBranch(br.prefix, br.branching, left, right)
}
// merge two maps in the current scope.
func (b *Builder) merge(c Collision, lhs, rhs node) node {
if lhs == rhs {
return lhs
}
switch lhs := lhs.(type) {
case *empty:
return rhs
case *leaf:
return b.insert(c, rhs, lhs, true)
case *branch:
switch rhs := rhs.(type) {
case *empty:
return lhs
case *leaf:
return b.insert(c, lhs, rhs, false)
case *branch:
// fallthrough
}
}
// Last remaining case is branch merging.
// For brevity, we adopt the Okasaki and Gill naming conventions
// for branching and prefixes.
s, t := lhs.(*branch), rhs.(*branch)
p, m := s.prefix, s.branching
q, n := t.prefix, t.branching
if m == n && p == q { // prefixes are identical.
left, right := b.merge(c, s.left, t.left), b.merge(c, s.right, t.right)
return b.mkBranch(p, m, left, right)
}
if !prefixesOverlap(p, m, q, n) {
return b.join(p, s, q, t) // prefixes are disjoint.
}
// prefixesOverlap(p, m, q, n) && !(m ==n && p == q)
// By prefixesOverlap(...), either:
// higher(m, n) && matchPrefix(q, p, m), or
// higher(n, m) && matchPrefix(p, q, n)
// So either s or t may can be merged with one branch or the other.
switch {
case ord(m, n) && zeroBit(q, m):
return b.mkBranch(p, m, b.merge(c, s.left, t), s.right)
case ord(m, n) && !zeroBit(q, m):
return b.mkBranch(p, m, s.left, b.merge(c, s.right, t))
case ord(n, m) && zeroBit(p, n):
return b.mkBranch(q, n, b.merge(c, s, t.left), t.right)
default:
return b.mkBranch(q, n, t.left, b.merge(c, s, t.right))
}
}
func (b *Builder) remove(m node, k key) node {
switch m := m.(type) {
case *empty:
return m
case *leaf:
if m.k == k {
return b.empty
}
return m
case *branch:
// fallthrough
}
br := m.(*branch)
kp := prefix(k)
if !matchPrefix(kp, br.prefix, br.branching) {
// The prefix does not match. kp is not in br.
return br
}
// the prefix matches. try to remove from the left or right branch.
left, right := br.left, br.right
if zeroBit(kp, br.branching) {
left = b.remove(left, k) // k may be in the left branch.
} else {
right = b.remove(right, k) // k may be in the right branch.
}
if left == br.left && right == br.right {
return br // no update
} else if _, ok := left.(*empty); ok {
return right // left updated and is empty.
} else if _, ok := right.(*empty); ok {
return left // right updated and is empty.
}
// Either left or right updated. Both left and right are not empty.
// The left and right branches still share the same prefix and disagree
// on the same branching bit. It is safe to directly create the branch.
return b.mkBranch(br.prefix, br.branching, left, right)
}
func (b *Builder) intersect(c Collision, l, r node) node {
if l == r {
return l
}
switch l := l.(type) {
case *empty:
return b.empty
case *leaf:
if rleaf := r.find(l.k); rleaf != nil {
return b.collide(c, l, rleaf)
}
return b.empty
case *branch:
switch r := r.(type) {
case *empty:
return b.empty
case *leaf:
if lleaf := l.find(r.k); lleaf != nil {
return b.collide(c, lleaf, r)
}
return b.empty
case *branch:
// fallthrough
}
}
// Last remaining case is branch intersection.
s, t := l.(*branch), r.(*branch)
p, m := s.prefix, s.branching
q, n := t.prefix, t.branching
if m == n && p == q {
// prefixes are identical.
left, right := b.intersect(c, s.left, t.left), b.intersect(c, s.right, t.right)
if _, ok := left.(*empty); ok {
return right
} else if _, ok := right.(*empty); ok {
return left
}
// The left and right branches are both non-empty.
// They still share the same prefix and disagree on the same branching bit.
// It is safe to directly create the branch.
return b.mkBranch(p, m, left, right)
}
if !prefixesOverlap(p, m, q, n) {
return b.empty // The prefixes share no keys.
}
// prefixesOverlap(p, m, q, n) && !(m ==n && p == q)
// By prefixesOverlap(...), either:
// ord(m, n) && matchPrefix(q, p, m), or
// ord(n, m) && matchPrefix(p, q, n)
// So either s or t may be a strict subtree of the other.
var lhs, rhs node
switch {
case ord(m, n) && zeroBit(q, m):
lhs, rhs = s.left, t
case ord(m, n) && !zeroBit(q, m):
lhs, rhs = s.right, t
case ord(n, m) && zeroBit(p, n):
lhs, rhs = s, t.left
default:
lhs, rhs = s, t.right
}
return b.intersect(c, lhs, rhs)
}

View File

@@ -0,0 +1,28 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package trie
import (
"strconv"
"sync/atomic"
)
// Scope represents a distinct collection of maps.
// Maps with the same Scope can be equal. Maps in different scopes are distinct.
// Each Builder creates maps within a unique Scope.
type Scope struct {
id int32
}
var nextScopeId int32
func newScope() Scope {
id := atomic.AddInt32(&nextScopeId, 1)
return Scope{id: id}
}
func (s Scope) String() string {
return strconv.Itoa(int(s.id))
}

View File

@@ -0,0 +1,229 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// trie implements persistent Patricia trie maps.
//
// Each Map is effectively a map from uint64 to interface{}. Patricia tries are
// a form of radix tree that are particularly appropriate when many maps will be
// created, merged together and large amounts of sharing are expected (e.g.
// environment abstract domains in program analysis).
//
// This implementation closely follows the paper:
//
// C. Okasaki and A. Gill, “Fast mergeable integer maps,” in ACM SIGPLAN
// Workshop on ML, September 1998, pp. 7786.
//
// Each Map is immutable and can be read from concurrently. The map does not
// guarantee that the value pointed to by the interface{} value is not updated
// concurrently.
//
// These Maps are optimized for situations where there will be many maps created at
// with a high degree of sharing and combining of maps together. If you do not expect,
// significant amount of sharing, the builtin map[T]U is much better choice!
//
// Each Map is created by a Builder. Each Builder has a unique Scope and each node is
// created within this scope. Maps x and y are == if they contains the same
// (key,value) mappings and have equal scopes.
//
// Internally these are big endian Patricia trie nodes, and the keys are sorted.
package trie
import (
"fmt"
"strings"
)
// Map is effectively a finite mapping from uint64 keys to interface{} values.
// Maps are immutable and can be read from concurrently.
//
// Notes on concurrency:
// - A Map value itself is an interface and assignments to a Map value can race.
// - Map does not guarantee that the value pointed to by the interface{} value
// is not updated concurrently.
type Map struct {
s Scope
n node
}
func (m Map) Scope() Scope {
return m.s
}
func (m Map) Size() int {
if m.n == nil {
return 0
}
return m.n.size()
}
func (m Map) Lookup(k uint64) (any, bool) {
if m.n != nil {
if leaf := m.n.find(key(k)); leaf != nil {
return leaf.v, true
}
}
return nil, false
}
// Converts the map into a {<key>: <value>[, ...]} string. This uses the default
// %s string conversion for <value>.
func (m Map) String() string {
var kvs []string
m.Range(func(u uint64, i any) bool {
kvs = append(kvs, fmt.Sprintf("%d: %s", u, i))
return true
})
return fmt.Sprintf("{%s}", strings.Join(kvs, ", "))
}
// Range over the leaf (key, value) pairs in the map in order and
// applies cb(key, value) to each. Stops early if cb returns false.
// Returns true if all elements were visited without stopping early.
func (m Map) Range(cb func(uint64, any) bool) bool {
if m.n != nil {
return m.n.visit(cb)
}
return true
}
// DeepEqual returns true if m and other contain the same (k, v) mappings
// [regardless of Scope].
//
// Equivalently m.DeepEqual(other) <=> reflect.DeepEqual(Elems(m), Elems(other))
func (m Map) DeepEqual(other Map) bool {
if m.Scope() == other.Scope() {
return m.n == other.n
}
if (m.n == nil) || (other.n == nil) {
return m.Size() == 0 && other.Size() == 0
}
return m.n.deepEqual(other.n)
}
// Elems are the (k,v) elements in the Map as a map[uint64]interface{}
func Elems(m Map) map[uint64]any {
dest := make(map[uint64]any, m.Size())
m.Range(func(k uint64, v any) bool {
dest[k] = v
return true
})
return dest
}
// node is an internal node within a trie map.
// A node is either empty, a leaf or a branch.
type node interface {
size() int
// visit the leaves (key, value) pairs in the map in order and
// applies cb(key, value) to each. Stops early if cb returns false.
// Returns true if all elements were visited without stopping early.
visit(cb func(uint64, any) bool) bool
// Two nodes contain the same elements regardless of scope.
deepEqual(node) bool
// find the leaf for the given key value or nil if it is not present.
find(k key) *leaf
// implementations must implement this.
nodeImpl()
}
// empty represents the empty map within a scope.
//
// The current builder ensure
type empty struct {
s Scope
}
// leaf represents a single <key, value> pair.
type leaf struct {
k key
v any
}
// branch represents a tree node within the Patricia trie.
//
// All keys within the branch match a `prefix` of the key
// up to a `branching` bit, and the left and right nodes
// contain keys that disagree on the bit at the `branching` bit.
type branch struct {
sz int // size. cached for O(1) lookup
prefix prefix // == mask(p0, branching) for some p0
branching bitpos
// Invariants:
// - neither is nil.
// - neither is *empty.
// - all keys in left are <= p.
// - all keys in right are > p.
left, right node
}
// all of these types are Maps.
var _ node = &empty{}
var _ node = &leaf{}
var _ node = &branch{}
func (*empty) nodeImpl() {}
func (*leaf) nodeImpl() {}
func (*branch) nodeImpl() {}
func (*empty) find(k key) *leaf { return nil }
func (l *leaf) find(k key) *leaf {
if k == l.k {
return l
}
return nil
}
func (br *branch) find(k key) *leaf {
kp := prefix(k)
if !matchPrefix(kp, br.prefix, br.branching) {
return nil
}
if zeroBit(kp, br.branching) {
return br.left.find(k)
}
return br.right.find(k)
}
func (*empty) size() int { return 0 }
func (*leaf) size() int { return 1 }
func (br *branch) size() int { return br.sz }
func (*empty) deepEqual(m node) bool {
_, ok := m.(*empty)
return ok
}
func (l *leaf) deepEqual(m node) bool {
if m, ok := m.(*leaf); ok {
return m == l || (l.k == m.k && l.v == m.v)
}
return false
}
func (br *branch) deepEqual(m node) bool {
if m, ok := m.(*branch); ok {
if br == m {
return true
}
return br.sz == m.sz && br.branching == m.branching && br.prefix == m.prefix &&
br.left.deepEqual(m.left) && br.right.deepEqual(m.right)
}
// if m is not a branch, m contains 0 or 1 elem.
// br contains at least 2 keys that disagree on a prefix.
return false
}
func (*empty) visit(cb func(uint64, any) bool) bool {
return true
}
func (l *leaf) visit(cb func(uint64, any) bool) bool {
return cb(uint64(l.k), l.v)
}
func (br *branch) visit(cb func(uint64, any) bool) bool {
if !br.left.visit(cb) {
return false
}
return br.right.visit(cb)
}

View File

@@ -0,0 +1,201 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vta
import (
"go/types"
"iter"
"slices"
"golang.org/x/tools/go/callgraph/vta/internal/trie"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types/typeutil"
)
// scc computes strongly connected components (SCCs) of `g` using the
// classical Tarjan's algorithm for SCCs. The result is two slices:
// - sccs: the SCCs, each represented as a slice of node indices
// - idxToSccID: the inverse map, from node index to SCC number.
//
// The SCCs are sorted in reverse topological order: for SCCs
// with ids X and Y s.t. X < Y, Y comes before X in the topological order.
func scc(g *vtaGraph) (sccs [][]idx, idxToSccID []int) {
// standard data structures used by Tarjan's algorithm.
type state struct {
pre int // preorder of the node (0 if unvisited)
lowLink int
onStack bool
}
states := make([]state, g.numNodes())
var stack []idx
idxToSccID = make([]int, g.numNodes())
nextPre := 0
var doSCC func(idx)
doSCC = func(n idx) {
nextPre++
ns := &states[n]
*ns = state{pre: nextPre, lowLink: nextPre, onStack: true}
stack = append(stack, n)
for s := range g.successors(n) {
if ss := &states[s]; ss.pre == 0 {
// Analyze successor s that has not been visited yet.
doSCC(s)
ns.lowLink = min(ns.lowLink, ss.lowLink)
} else if ss.onStack {
// The successor is on the stack, meaning it has to be
// in the current SCC.
ns.lowLink = min(ns.lowLink, ss.pre)
}
}
// if n is a root node, pop the stack and generate a new SCC.
if ns.lowLink == ns.pre {
sccStart := slicesLastIndex(stack, n)
scc := slices.Clone(stack[sccStart:])
stack = stack[:sccStart]
sccID := len(sccs)
sccs = append(sccs, scc)
for _, w := range scc {
states[w].onStack = false
idxToSccID[w] = sccID
}
}
}
for n, nn := 0, g.numNodes(); n < nn; n++ {
if states[n].pre == 0 {
doSCC(idx(n))
}
}
return sccs, idxToSccID
}
// slicesLastIndex returns the index of the last occurrence of v in s, or -1 if v is
// not present in s.
//
// slicesLastIndex iterates backwards through the elements of s, stopping when the ==
// operator determines an element is equal to v.
func slicesLastIndex[S ~[]E, E comparable](s S, v E) int {
// TODO: move to / dedup with slices.LastIndex
for i := len(s) - 1; i >= 0; i-- {
if s[i] == v {
return i
}
}
return -1
}
// propType represents type information being propagated
// over the vta graph. f != nil only for function nodes
// and nodes reachable from function nodes. There, we also
// remember the actual *ssa.Function in order to more
// precisely model higher-order flow.
type propType struct {
typ types.Type
f *ssa.Function
}
// propTypeMap is an auxiliary structure that serves
// the role of a map from nodes to a set of propTypes.
type propTypeMap map[node]*trie.MutMap
// propTypes returns an iterator for the propTypes associated with
// node `n` in map `ptm`.
func (ptm propTypeMap) propTypes(n node) iter.Seq[propType] {
return func(yield func(propType) bool) {
if types := ptm[n]; types != nil {
types.M.Range(func(_ uint64, elem any) bool {
return yield(elem.(propType))
})
}
}
}
// propagate reduces the `graph` based on its SCCs and
// then propagates type information through the reduced
// graph. The result is a map from nodes to a set of types
// and functions, stemming from higher-order data flow,
// reaching the node. `canon` is used for type uniqueness.
func propagate(graph *vtaGraph, canon *typeutil.Map) propTypeMap {
sccs, idxToSccID := scc(graph)
// propTypeIds are used to create unique ids for
// propType, to be used for trie-based type sets.
propTypeIds := make(map[propType]uint64)
// Id creation is based on == equality, which works
// as types are canonicalized (see getPropType).
propTypeId := func(p propType) uint64 {
if id, ok := propTypeIds[p]; ok {
return id
}
id := uint64(len(propTypeIds))
propTypeIds[p] = id
return id
}
builder := trie.NewBuilder()
// Initialize sccToTypes to avoid repeated check
// for initialization later.
sccToTypes := make([]*trie.MutMap, len(sccs))
for sccID, scc := range sccs {
typeSet := builder.MutEmpty()
for _, idx := range scc {
if n := graph.node[idx]; hasInitialTypes(n) {
// add the propType for idx to typeSet.
pt := getPropType(n, canon)
typeSet.Update(propTypeId(pt), pt)
}
}
sccToTypes[sccID] = &typeSet
}
for i := len(sccs) - 1; i >= 0; i-- {
nextSccs := make(map[int]empty)
for _, n := range sccs[i] {
for succ := range graph.successors(n) {
nextSccs[idxToSccID[succ]] = empty{}
}
}
// Propagate types to all successor SCCs.
for nextScc := range nextSccs {
sccToTypes[nextScc].Merge(sccToTypes[i].M)
}
}
nodeToTypes := make(propTypeMap, graph.numNodes())
for sccID, scc := range sccs {
types := sccToTypes[sccID]
for _, idx := range scc {
nodeToTypes[graph.node[idx]] = types
}
}
return nodeToTypes
}
// hasInitialTypes check if a node can have initial types.
// Returns true iff `n` is not a panic, recover, nestedPtr*
// node, nor a node whose type is an interface.
func hasInitialTypes(n node) bool {
switch n.(type) {
case panicArg, recoverReturn, nestedPtrFunction, nestedPtrInterface:
return false
default:
return !types.IsInterface(n.Type())
}
}
// getPropType creates a propType for `node` based on its type.
// propType.typ is always node.Type(). If node is function, then
// propType.val is the underlying function; nil otherwise.
func getPropType(node node, canon *typeutil.Map) propType {
t := canonicalize(node.Type(), canon)
if fn, ok := node.(function); ok {
return propType{f: fn.f, typ: t}
}
return propType{f: nil, typ: t}
}

188
vendor/golang.org/x/tools/go/callgraph/vta/utils.go generated vendored Normal file
View File

@@ -0,0 +1,188 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vta
import (
"go/types"
"iter"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/internal/typeparams"
)
func canAlias(n1, n2 node) bool {
return isReferenceNode(n1) && isReferenceNode(n2)
}
func isReferenceNode(n node) bool {
if _, ok := n.(nestedPtrInterface); ok {
return true
}
if _, ok := n.(nestedPtrFunction); ok {
return true
}
if _, ok := types.Unalias(n.Type()).(*types.Pointer); ok {
return true
}
return false
}
// hasInFlow checks if a concrete type can flow to node `n`.
// Returns yes iff the type of `n` satisfies one the following:
// 1. is an interface
// 2. is a (nested) pointer to interface (needed for, say,
// slice elements of nested pointers to interface type)
// 3. is a function type (needed for higher-order type flow)
// 4. is a (nested) pointer to function (needed for, say,
// slice elements of nested pointers to function type)
// 5. is a global Recover or Panic node
func hasInFlow(n node) bool {
if _, ok := n.(panicArg); ok {
return true
}
if _, ok := n.(recoverReturn); ok {
return true
}
t := n.Type()
if i := interfaceUnderPtr(t); i != nil {
return true
}
if f := functionUnderPtr(t); f != nil {
return true
}
return types.IsInterface(t) || isFunction(t)
}
func isFunction(t types.Type) bool {
_, ok := t.Underlying().(*types.Signature)
return ok
}
// interfaceUnderPtr checks if type `t` is a potentially nested
// pointer to interface and if yes, returns the interface type.
// Otherwise, returns nil.
func interfaceUnderPtr(t types.Type) types.Type {
seen := make(map[types.Type]bool)
var visit func(types.Type) types.Type
visit = func(t types.Type) types.Type {
if seen[t] {
return nil
}
seen[t] = true
p, ok := t.Underlying().(*types.Pointer)
if !ok {
return nil
}
if types.IsInterface(p.Elem()) {
return p.Elem()
}
return visit(p.Elem())
}
return visit(t)
}
// functionUnderPtr checks if type `t` is a potentially nested
// pointer to function type and if yes, returns the function type.
// Otherwise, returns nil.
func functionUnderPtr(t types.Type) types.Type {
seen := make(map[types.Type]bool)
var visit func(types.Type) types.Type
visit = func(t types.Type) types.Type {
if seen[t] {
return nil
}
seen[t] = true
p, ok := t.Underlying().(*types.Pointer)
if !ok {
return nil
}
if isFunction(p.Elem()) {
return p.Elem()
}
return visit(p.Elem())
}
return visit(t)
}
// sliceArrayElem returns the element type of type `t` that is
// expected to be a (pointer to) array, slice or string, consistent with
// the ssa.Index and ssa.IndexAddr instructions. Panics otherwise.
func sliceArrayElem(t types.Type) types.Type {
switch u := t.Underlying().(type) {
case *types.Pointer:
switch e := u.Elem().Underlying().(type) {
case *types.Array:
return e.Elem()
case *types.Interface:
return sliceArrayElem(e) // e is a type param with matching element types.
default:
panic(t)
}
case *types.Array:
return u.Elem()
case *types.Slice:
return u.Elem()
case *types.Basic:
return types.Typ[types.Byte]
case *types.Interface: // type param.
terms, err := typeparams.InterfaceTermSet(u)
if err != nil || len(terms) == 0 {
panic(t)
}
return sliceArrayElem(terms[0].Type()) // Element types must match.
default:
panic(t)
}
}
// siteCallees returns an iterator for the callees for call site `c`.
func siteCallees(c ssa.CallInstruction, callees calleesFunc) iter.Seq[*ssa.Function] {
return func(yield func(*ssa.Function) bool) {
for _, callee := range callees(c) {
if !yield(callee) {
return
}
}
}
}
func canHaveMethods(t types.Type) bool {
t = types.Unalias(t)
if _, ok := t.(*types.Named); ok {
return true
}
u := t.Underlying()
switch u.(type) {
case *types.Interface, *types.Signature, *types.Struct:
return true
default:
return false
}
}
// calls returns the set of call instructions in `f`.
func calls(f *ssa.Function) []ssa.CallInstruction {
var calls []ssa.CallInstruction
for _, bl := range f.Blocks {
for _, instr := range bl.Instrs {
if c, ok := instr.(ssa.CallInstruction); ok {
calls = append(calls, c)
}
}
}
return calls
}

190
vendor/golang.org/x/tools/go/callgraph/vta/vta.go generated vendored Normal file
View File

@@ -0,0 +1,190 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package vta computes the call graph of a Go program using the Variable
// Type Analysis (VTA) algorithm originally described in "Practical Virtual
// Method Call Resolution for Java," Vijay Sundaresan, Laurie Hendren,
// Chrislain Razafimahefa, Raja Vallée-Rai, Patrick Lam, Etienne Gagnon, and
// Charles Godin.
//
// Note: this package is in experimental phase and its interface is
// subject to change.
// TODO(zpavlinovic): reiterate on documentation.
//
// The VTA algorithm overapproximates the set of types (and function literals)
// a variable can take during runtime by building a global type propagation
// graph and propagating types (and function literals) through the graph.
//
// A type propagation is a directed, labeled graph. A node can represent
// one of the following:
// - A field of a struct type.
// - A local (SSA) variable of a method/function.
// - All pointers to a non-interface type.
// - The return value of a method.
// - All elements in an array.
// - All elements in a slice.
// - All elements in a map.
// - All elements in a channel.
// - A global variable.
//
// In addition, the implementation used in this package introduces
// a few Go specific kinds of nodes:
// - (De)references of nested pointers to interfaces are modeled
// as a unique nestedPtrInterface node in the type propagation graph.
// - Each function literal is represented as a function node whose
// internal value is the (SSA) representation of the function. This
// is done to precisely infer flow of higher-order functions.
//
// Edges in the graph represent flow of types (and function literals) through
// the program. That is, the model 1) typing constraints that are induced by
// assignment statements or function and method calls and 2) higher-order flow
// of functions in the program.
//
// The labeling function maps each node to a set of types and functions that
// can intuitively reach the program construct the node represents. Initially,
// every node is assigned a type corresponding to the program construct it
// represents. Function nodes are also assigned the function they represent.
// The labeling function then propagates types and function through the graph.
//
// The result of VTA is a type propagation graph in which each node is labeled
// with a conservative overapproximation of the set of types (and functions)
// it may have. This information is then used to construct the call graph.
// For each unresolved call site, vta uses the set of types and functions
// reaching the node representing the call site to create a set of callees.
package vta
// TODO(zpavlinovic): update VTA for how it handles generic function bodies and instantiation wrappers.
import (
"go/types"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
)
// CallGraph uses the VTA algorithm to compute call graph for all functions
// f:true in funcs. VTA refines the results of initial call graph and uses it
// to establish interprocedural type flow. If initial is nil, VTA uses a more
// efficient approach to construct a CHA call graph.
//
// The resulting graph does not have a root node.
//
// CallGraph does not make any assumptions on initial types global variables
// and function/method inputs can have. CallGraph is then sound, modulo use of
// reflection and unsafe, if the initial call graph is sound.
func CallGraph(funcs map[*ssa.Function]bool, initial *callgraph.Graph) *callgraph.Graph {
callees := makeCalleesFunc(funcs, initial)
vtaG, canon := typePropGraph(funcs, callees)
types := propagate(vtaG, canon)
c := &constructor{types: types, callees: callees, cache: make(methodCache)}
return c.construct(funcs)
}
// constructor type linearly traverses the input program
// and constructs a callgraph based on the results of the
// VTA type propagation phase.
type constructor struct {
types propTypeMap
cache methodCache
callees calleesFunc
}
func (c *constructor) construct(funcs map[*ssa.Function]bool) *callgraph.Graph {
cg := &callgraph.Graph{Nodes: make(map[*ssa.Function]*callgraph.Node)}
for f, in := range funcs {
if in {
c.constrct(cg, f)
}
}
return cg
}
func (c *constructor) constrct(g *callgraph.Graph, f *ssa.Function) {
caller := g.CreateNode(f)
for _, call := range calls(f) {
for _, c := range c.resolves(call) {
callgraph.AddEdge(caller, call, g.CreateNode(c))
}
}
}
// resolves computes the set of functions to which VTA resolves `c`. The resolved
// functions are intersected with functions to which `c.initial` resolves `c`.
func (c *constructor) resolves(call ssa.CallInstruction) []*ssa.Function {
cc := call.Common()
if cc.StaticCallee() != nil {
return []*ssa.Function{cc.StaticCallee()}
}
// Skip builtins as they are not *ssa.Function.
if _, ok := cc.Value.(*ssa.Builtin); ok {
return nil
}
// Cover the case of dynamic higher-order and interface calls.
var res []*ssa.Function
resolved := resolve(call, c.types, c.cache)
for f := range siteCallees(call, c.callees) {
if _, ok := resolved[f]; ok {
res = append(res, f)
}
}
return res
}
// resolve returns a set of functions `c` resolves to based on the
// type propagation results in `types`.
func resolve(c ssa.CallInstruction, types propTypeMap, cache methodCache) map[*ssa.Function]empty {
fns := make(map[*ssa.Function]empty)
n := local{val: c.Common().Value}
for p := range types.propTypes(n) {
for _, f := range propFunc(p, c, cache) {
fns[f] = empty{}
}
}
return fns
}
// propFunc returns the functions modeled with the propagation type `p`
// assigned to call site `c`. If no such function exists, nil is returned.
func propFunc(p propType, c ssa.CallInstruction, cache methodCache) []*ssa.Function {
if p.f != nil {
return []*ssa.Function{p.f}
}
if c.Common().Method == nil {
return nil
}
return cache.methods(p.typ, c.Common().Method.Name(), c.Parent().Prog)
}
// methodCache serves as a type -> method name -> methods
// cache when computing methods of a type using the
// ssa.Program.MethodSets and ssa.Program.MethodValue
// APIs. The cache is used to speed up querying of
// methods of a type as the mentioned APIs are expensive.
type methodCache map[types.Type]map[string][]*ssa.Function
// methods returns methods of a type `t` named `name`. First consults
// `mc` and otherwise queries `prog` for the method. If no such method
// exists, nil is returned.
func (mc methodCache) methods(t types.Type, name string, prog *ssa.Program) []*ssa.Function {
if ms, ok := mc[t]; ok {
return ms[name]
}
ms := make(map[string][]*ssa.Function)
mset := prog.MethodSets.MethodSet(t)
for i, n := 0, mset.Len(); i < n; i++ {
// f can be nil when t is an interface or some
// other type without any runtime methods.
if f := prog.MethodValue(mset.At(i)); f != nil {
ms[f.Name()] = append(ms[f.Name()], f)
}
}
mc[t] = ms
return ms[name]
}

View File

@@ -0,0 +1,236 @@
// Copyright 2016 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 gcexportdata provides functions for reading and writing
// export data, which is a serialized description of the API of a Go
// package including the names, kinds, types, and locations of all
// exported declarations.
//
// The standard Go compiler (cmd/compile) writes an export data file
// for each package it compiles, which it later reads when compiling
// packages that import the earlier one. The compiler must thus
// contain logic to both write and read export data.
// (See the "Export" section in the cmd/compile/README file.)
//
// The [Read] function in this package can read files produced by the
// compiler, producing [go/types] data structures. As a matter of
// policy, Read supports export data files produced by only the last
// two Go releases plus tip; see https://go.dev/issue/68898. The
// export data files produced by the compiler contain additional
// details related to generics, inlining, and other optimizations that
// cannot be decoded by the [Read] function.
//
// In files written by the compiler, the export data is not at the
// start of the file. Before calling Read, use [NewReader] to locate
// the desired portion of the file.
//
// The [Write] function in this package encodes the exported API of a
// Go package ([types.Package]) as a file. Such files can be later
// decoded by Read, but cannot be consumed by the compiler.
//
// # Future changes
//
// Although Read supports the formats written by both Write and the
// compiler, the two are quite different, and there is an open
// proposal (https://go.dev/issue/69491) to separate these APIs.
//
// Under that proposal, this package would ultimately provide only the
// Read operation for compiler export data, which must be defined in
// this module (golang.org/x/tools), not in the standard library, to
// avoid version skew for developer tools that need to read compiler
// export data both before and after a Go release, such as from Go
// 1.23 to Go 1.24. Because this package lives in the tools module,
// clients can update their version of the module some time before the
// Go 1.24 release and rebuild and redeploy their tools, which will
// then be able to consume both Go 1.23 and Go 1.24 export data files,
// so they will work before and after the Go update. (See discussion
// at https://go.dev/issue/15651.)
//
// The operations to import and export [go/types] data structures
// would be defined in the go/types package as Import and Export.
// [Write] would (eventually) delegate to Export,
// and [Read], when it detects a file produced by Export,
// would delegate to Import.
//
// # Deprecations
//
// The [NewImporter] and [Find] functions are deprecated and should
// not be used in new code. The [WriteBundle] and [ReadBundle]
// functions are experimental, and there is an open proposal to
// deprecate them (https://go.dev/issue/69573).
package gcexportdata
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"go/token"
"go/types"
"io"
"os/exec"
"golang.org/x/tools/internal/gcimporter"
)
// Find returns the name of an object (.o) or archive (.a) file
// containing type information for the specified import path,
// using the go command.
// If no file was found, an empty filename is returned.
//
// A relative srcDir is interpreted relative to the current working directory.
//
// Find also returns the package's resolved (canonical) import path,
// reflecting the effects of srcDir and vendoring on importPath.
//
// Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
// which is more efficient.
func Find(importPath, srcDir string) (filename, path string) {
cmd := exec.Command("go", "list", "-json", "-export", "--", importPath)
cmd.Dir = srcDir
out, err := cmd.Output()
if err != nil {
return "", ""
}
var data struct {
ImportPath string
Export string
}
json.Unmarshal(out, &data)
return data.Export, data.ImportPath
}
// NewReader returns a reader for the export data section of an object
// (.o) or archive (.a) file read from r. The new reader may provide
// additional trailing data beyond the end of the export data.
func NewReader(r io.Reader) (io.Reader, error) {
buf := bufio.NewReader(r)
size, err := gcimporter.FindExportData(buf)
if err != nil {
return nil, err
}
// We were given an archive and found the __.PKGDEF in it.
// This tells us the size of the export data, and we don't
// need to return the entire file.
return &io.LimitedReader{
R: buf,
N: size,
}, nil
}
// readAll works the same way as io.ReadAll, but avoids allocations and copies
// by preallocating a byte slice of the necessary size if the size is known up
// front. This is always possible when the input is an archive. In that case,
// NewReader will return the known size using an io.LimitedReader.
func readAll(r io.Reader) ([]byte, error) {
if lr, ok := r.(*io.LimitedReader); ok {
data := make([]byte, lr.N)
_, err := io.ReadFull(lr, data)
return data, err
}
return io.ReadAll(r)
}
// Read reads export data from in, decodes it, and returns type
// information for the package.
//
// Read is capable of reading export data produced by [Write] at the
// same source code version, or by the last two Go releases (plus tip)
// of the standard Go compiler. Reading files from older compilers may
// produce an error.
//
// The package path (effectively its linker symbol prefix) is
// specified by path, since unlike the package name, this information
// may not be recorded in the export data.
//
// File position information is added to fset.
//
// Read may inspect and add to the imports map to ensure that references
// within the export data to other packages are consistent. The caller
// must ensure that imports[path] does not exist, or exists but is
// incomplete (see types.Package.Complete), and Read inserts the
// resulting package into this map entry.
//
// On return, the state of the reader is undefined.
func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
data, err := readAll(in)
if err != nil {
return nil, fmt.Errorf("reading export data for %q: %v", path, err)
}
if bytes.HasPrefix(data, []byte("!<arch>")) {
return nil, fmt.Errorf("can't read export data for %q directly from an archive file (call gcexportdata.NewReader first to extract export data)", path)
}
// The indexed export format starts with an 'i'; the older
// binary export format starts with a 'c', 'd', or 'v'
// (from "version"). Select appropriate importer.
if len(data) > 0 {
switch data[0] {
case 'v', 'c', 'd':
// binary, produced by cmd/compile till go1.10
return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0])
case 'i':
// indexed, produced by cmd/compile till go1.19,
// and also by [Write].
//
// If proposal #69491 is accepted, go/types
// serialization will be implemented by
// types.Export, to which Write would eventually
// delegate (explicitly dropping any pretence at
// inter-version Write-Read compatibility).
// This [Read] function would delegate to types.Import
// when it detects that the file was produced by Export.
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
return pkg, err
case 'u':
// unified, produced by cmd/compile since go1.20
_, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path)
return pkg, err
default:
l := min(len(data), 10)
return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), path)
}
}
return nil, fmt.Errorf("empty export data for %s", path)
}
// Write writes encoded type information for the specified package to out.
// The FileSet provides file position information for named objects.
func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
if _, err := io.WriteString(out, "i"); err != nil {
return err
}
return gcimporter.IExportData(out, fset, pkg)
}
// ReadBundle reads an export bundle from in, decodes it, and returns type
// information for the packages.
// File position information is added to fset.
//
// ReadBundle may inspect and add to the imports map to ensure that references
// within the export bundle to other packages are consistent.
//
// On return, the state of the reader is undefined.
//
// Experimental: This API is experimental and may change in the future.
func ReadBundle(in io.Reader, fset *token.FileSet, imports map[string]*types.Package) ([]*types.Package, error) {
data, err := readAll(in)
if err != nil {
return nil, fmt.Errorf("reading export bundle: %v", err)
}
return gcimporter.IImportBundle(fset, imports, data)
}
// WriteBundle writes encoded type information for the specified packages to out.
// The FileSet provides file position information for named objects.
//
// Experimental: This API is experimental and may change in the future.
func WriteBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error {
return gcimporter.IExportBundle(out, fset, pkgs)
}

75
vendor/golang.org/x/tools/go/gcexportdata/importer.go generated vendored Normal file
View File

@@ -0,0 +1,75 @@
// Copyright 2016 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 gcexportdata
import (
"fmt"
"go/token"
"go/types"
"os"
)
// NewImporter returns a new instance of the types.Importer interface
// that reads type information from export data files written by gc.
// The Importer also satisfies types.ImporterFrom.
//
// Export data files are located using "go build" workspace conventions
// and the build.Default context.
//
// Use this importer instead of go/importer.For("gc", ...) to avoid the
// version-skew problems described in the documentation of this package,
// or to control the FileSet or access the imports map populated during
// package loading.
//
// Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
// which is more efficient.
func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom {
return importer{fset, imports}
}
type importer struct {
fset *token.FileSet
imports map[string]*types.Package
}
func (imp importer) Import(importPath string) (*types.Package, error) {
return imp.ImportFrom(importPath, "", 0)
}
func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) {
filename, path := Find(importPath, srcDir)
if filename == "" {
if importPath == "unsafe" {
// Even for unsafe, call Find first in case
// the package was vendored.
return types.Unsafe, nil
}
return nil, fmt.Errorf("can't find import: %s", importPath)
}
if pkg, ok := imp.imports[path]; ok && pkg.Complete() {
return pkg, nil // cache hit
}
// open file
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer func() {
f.Close()
if err != nil {
// add file name to error
err = fmt.Errorf("reading export data: %s: %v", filename, err)
}
}()
r, err := NewReader(f)
if err != nil {
return nil, err
}
return Read(r, imp.fset, imp.imports, path)
}

219
vendor/golang.org/x/tools/go/internal/cgo/cgo.go generated vendored Normal file
View File

@@ -0,0 +1,219 @@
// Copyright 2013 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 cgo handles cgo preprocessing of files containing `import "C"`.
//
// DESIGN
//
// The approach taken is to run the cgo processor on the package's
// CgoFiles and parse the output, faking the filenames of the
// resulting ASTs so that the synthetic file containing the C types is
// called "C" (e.g. "~/go/src/net/C") and the preprocessed files
// have their original names (e.g. "~/go/src/net/cgo_unix.go"),
// not the names of the actual temporary files.
//
// The advantage of this approach is its fidelity to 'go build'. The
// downside is that the token.Position.Offset for each AST node is
// incorrect, being an offset within the temporary file. Line numbers
// should still be correct because of the //line comments.
//
// The logic of this file is mostly plundered from the 'go build'
// tool, which also invokes the cgo preprocessor.
//
//
// REJECTED ALTERNATIVE
//
// An alternative approach that we explored is to extend go/types'
// Importer mechanism to provide the identity of the importing package
// so that each time `import "C"` appears it resolves to a different
// synthetic package containing just the objects needed in that case.
// The loader would invoke cgo but parse only the cgo_types.go file
// defining the package-level objects, discarding the other files
// resulting from preprocessing.
//
// The benefit of this approach would have been that source-level
// syntax information would correspond exactly to the original cgo
// file, with no preprocessing involved, making source tools like
// godoc, guru, and eg happy. However, the approach was rejected
// due to the additional complexity it would impose on go/types. (It
// made for a beautiful demo, though.)
//
// cgo files, despite their *.go extension, are not legal Go source
// files per the specification since they may refer to unexported
// members of package "C" such as C.int. Also, a function such as
// C.getpwent has in effect two types, one matching its C type and one
// which additionally returns (errno C.int). The cgo preprocessor
// uses name mangling to distinguish these two functions in the
// processed code, but go/types would need to duplicate this logic in
// its handling of function calls, analogous to the treatment of map
// lookups in which y=m[k] and y,ok=m[k] are both legal.
package cgo
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
)
// ProcessFiles invokes the cgo preprocessor on bp.CgoFiles, parses
// the output and returns the resulting ASTs.
func ProcessFiles(bp *build.Package, fset *token.FileSet, DisplayPath func(path string) string, mode parser.Mode) ([]*ast.File, error) {
tmpdir, err := os.MkdirTemp("", strings.Replace(bp.ImportPath, "/", "_", -1)+"_C")
if err != nil {
return nil, err
}
defer os.RemoveAll(tmpdir)
pkgdir := bp.Dir
if DisplayPath != nil {
pkgdir = DisplayPath(pkgdir)
}
cgoFiles, cgoDisplayFiles, err := Run(bp, pkgdir, tmpdir, false)
if err != nil {
return nil, err
}
var files []*ast.File
for i := range cgoFiles {
rd, err := os.Open(cgoFiles[i])
if err != nil {
return nil, err
}
display := filepath.Join(bp.Dir, cgoDisplayFiles[i])
f, err := parser.ParseFile(fset, display, rd, mode)
rd.Close()
if err != nil {
return nil, err
}
files = append(files, f)
}
return files, nil
}
var cgoRe = regexp.MustCompile(`[/\\:]`)
// Run invokes the cgo preprocessor on bp.CgoFiles and returns two
// lists of files: the resulting processed files (in temporary
// directory tmpdir) and the corresponding names of the unprocessed files.
//
// Run is adapted from (*builder).cgo in
// $GOROOT/src/cmd/go/build.go, but these features are unsupported:
// Objective C, CGOPKGPATH, CGO_FLAGS.
//
// If useabs is set to true, absolute paths of the bp.CgoFiles will be passed in
// to the cgo preprocessor. This in turn will set the // line comments
// referring to those files to use absolute paths. This is needed for
// go/packages using the legacy go list support so it is able to find
// the original files.
func Run(bp *build.Package, pkgdir, tmpdir string, useabs bool) (files, displayFiles []string, err error) {
cgoCPPFLAGS, _, _, _ := cflags(bp, true)
_, cgoexeCFLAGS, _, _ := cflags(bp, false)
if len(bp.CgoPkgConfig) > 0 {
pcCFLAGS, err := pkgConfigFlags(bp)
if err != nil {
return nil, nil, err
}
cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...)
}
// Allows including _cgo_export.h from .[ch] files in the package.
cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", tmpdir)
// _cgo_gotypes.go (displayed "C") contains the type definitions.
files = append(files, filepath.Join(tmpdir, "_cgo_gotypes.go"))
displayFiles = append(displayFiles, "C")
for _, fn := range bp.CgoFiles {
// "foo.cgo1.go" (displayed "foo.go") is the processed Go source.
f := cgoRe.ReplaceAllString(fn[:len(fn)-len("go")], "_")
files = append(files, filepath.Join(tmpdir, f+"cgo1.go"))
displayFiles = append(displayFiles, fn)
}
var cgoflags []string
if bp.Goroot && bp.ImportPath == "runtime/cgo" {
cgoflags = append(cgoflags, "-import_runtime_cgo=false")
}
if bp.Goroot && bp.ImportPath == "runtime/race" || bp.ImportPath == "runtime/cgo" {
cgoflags = append(cgoflags, "-import_syscall=false")
}
var cgoFiles []string = bp.CgoFiles
if useabs {
cgoFiles = make([]string, len(bp.CgoFiles))
for i := range cgoFiles {
cgoFiles[i] = filepath.Join(pkgdir, bp.CgoFiles[i])
}
}
args := stringList(
"go", "tool", "cgo", "-objdir", tmpdir, cgoflags, "--",
cgoCPPFLAGS, cgoexeCFLAGS, cgoFiles,
)
if false {
log.Printf("Running cgo for package %q: %s (dir=%s)", bp.ImportPath, args, pkgdir)
}
cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = pkgdir
cmd.Env = append(os.Environ(), "PWD="+pkgdir)
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return nil, nil, fmt.Errorf("cgo failed: %s: %s", args, err)
}
return files, displayFiles, nil
}
// -- unmodified from 'go build' ---------------------------------------
// Return the flags to use when invoking the C or C++ compilers, or cgo.
func cflags(p *build.Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) {
var defaults string
if def {
defaults = "-g -O2"
}
cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS)
cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS)
cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS)
ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS)
return
}
// envList returns the value of the given environment variable broken
// into fields, using the default value when the variable is empty.
func envList(key, def string) []string {
v := os.Getenv(key)
if v == "" {
v = def
}
return strings.Fields(v)
}
// stringList's arguments should be a sequence of string or []string values.
// stringList flattens them into a single []string.
func stringList(args ...any) []string {
var x []string
for _, arg := range args {
switch arg := arg.(type) {
case []string:
x = append(x, arg...)
case string:
x = append(x, arg)
default:
panic("stringList: invalid argument")
}
}
return x
}

View File

@@ -0,0 +1,42 @@
// Copyright 2013 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 cgo
import (
"errors"
"fmt"
"go/build"
"os/exec"
"strings"
)
// pkgConfig runs pkg-config with the specified arguments and returns the flags it prints.
func pkgConfig(mode string, pkgs []string) (flags []string, err error) {
cmd := exec.Command("pkg-config", append([]string{mode}, pkgs...)...)
out, err := cmd.Output()
if err != nil {
s := fmt.Sprintf("%s failed: %v", strings.Join(cmd.Args, " "), err)
if len(out) > 0 {
s = fmt.Sprintf("%s: %s", s, out)
}
if err, ok := err.(*exec.ExitError); ok && len(err.Stderr) > 0 {
s = fmt.Sprintf("%s\nstderr:\n%s", s, err.Stderr)
}
return nil, errors.New(s)
}
if len(out) > 0 {
flags = strings.Fields(string(out))
}
return
}
// pkgConfigFlags calls pkg-config if needed and returns the cflags
// needed to build the package.
func pkgConfigFlags(p *build.Package) (cflags []string, err error) {
if len(p.CgoPkgConfig) == 0 {
return nil, nil
}
return pkgConfig("--cflags", p.CgoPkgConfig)
}

202
vendor/golang.org/x/tools/go/loader/doc.go generated vendored Normal file
View File

@@ -0,0 +1,202 @@
// 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.
// Package loader loads a complete Go program from source code, parsing
// and type-checking the initial packages plus their transitive closure
// of dependencies. The ASTs and the derived facts are retained for
// later use.
//
// Deprecated: This is an older API and does not have support
// for modules. Use golang.org/x/tools/go/packages instead.
//
// The package defines two primary types: Config, which specifies a
// set of initial packages to load and various other options; and
// Program, which is the result of successfully loading the packages
// specified by a configuration.
//
// The configuration can be set directly, but *Config provides various
// convenience methods to simplify the common cases, each of which can
// be called any number of times. Finally, these are followed by a
// call to Load() to actually load and type-check the program.
//
// var conf loader.Config
//
// // Use the command-line arguments to specify
// // a set of initial packages to load from source.
// // See FromArgsUsage for help.
// rest, err := conf.FromArgs(os.Args[1:], wantTests)
//
// // Parse the specified files and create an ad hoc package with path "foo".
// // All files must have the same 'package' declaration.
// conf.CreateFromFilenames("foo", "foo.go", "bar.go")
//
// // Create an ad hoc package with path "foo" from
// // the specified already-parsed files.
// // All ASTs must have the same 'package' declaration.
// conf.CreateFromFiles("foo", parsedFiles)
//
// // Add "runtime" to the set of packages to be loaded.
// conf.Import("runtime")
//
// // Adds "fmt" and "fmt_test" to the set of packages
// // to be loaded. "fmt" will include *_test.go files.
// conf.ImportWithTests("fmt")
//
// // Finally, load all the packages specified by the configuration.
// prog, err := conf.Load()
//
// See examples_test.go for examples of API usage.
//
// # CONCEPTS AND TERMINOLOGY
//
// The WORKSPACE is the set of packages accessible to the loader. The
// workspace is defined by Config.Build, a *build.Context. The
// default context treats subdirectories of $GOROOT and $GOPATH as
// packages, but this behavior may be overridden.
//
// An AD HOC package is one specified as a set of source files on the
// command line. In the simplest case, it may consist of a single file
// such as $GOROOT/src/net/http/triv.go.
//
// EXTERNAL TEST packages are those comprised of a set of *_test.go
// files all with the same 'package foo_test' declaration, all in the
// same directory. (go/build.Package calls these files XTestFiles.)
//
// An IMPORTABLE package is one that can be referred to by some import
// spec. Every importable package is uniquely identified by its
// PACKAGE PATH or just PATH, a string such as "fmt", "encoding/json",
// or "cmd/vendor/golang.org/x/arch/x86/x86asm". A package path
// typically denotes a subdirectory of the workspace.
//
// An import declaration uses an IMPORT PATH to refer to a package.
// Most import declarations use the package path as the import path.
//
// Due to VENDORING (https://golang.org/s/go15vendor), the
// interpretation of an import path may depend on the directory in which
// it appears. To resolve an import path to a package path, go/build
// must search the enclosing directories for a subdirectory named
// "vendor".
//
// ad hoc packages and external test packages are NON-IMPORTABLE. The
// path of an ad hoc package is inferred from the package
// declarations of its files and is therefore not a unique package key.
// For example, Config.CreatePkgs may specify two initial ad hoc
// packages, both with path "main".
//
// An AUGMENTED package is an importable package P plus all the
// *_test.go files with same 'package foo' declaration as P.
// (go/build.Package calls these files TestFiles.)
//
// The INITIAL packages are those specified in the configuration. A
// DEPENDENCY is a package loaded to satisfy an import in an initial
// package or another dependency.
package loader
// IMPLEMENTATION NOTES
//
// 'go test', in-package test files, and import cycles
// ---------------------------------------------------
//
// An external test package may depend upon members of the augmented
// package that are not in the unaugmented package, such as functions
// that expose internals. (See bufio/export_test.go for an example.)
// So, the loader must ensure that for each external test package
// it loads, it also augments the corresponding non-test package.
//
// The import graph over n unaugmented packages must be acyclic; the
// import graph over n-1 unaugmented packages plus one augmented
// package must also be acyclic. ('go test' relies on this.) But the
// import graph over n augmented packages may contain cycles.
//
// First, all the (unaugmented) non-test packages and their
// dependencies are imported in the usual way; the loader reports an
// error if it detects an import cycle.
//
// Then, each package P for which testing is desired is augmented by
// the list P' of its in-package test files, by calling
// (*types.Checker).Files. This arrangement ensures that P' may
// reference definitions within P, but P may not reference definitions
// within P'. Furthermore, P' may import any other package, including
// ones that depend upon P, without an import cycle error.
//
// Consider two packages A and B, both of which have lists of
// in-package test files we'll call A' and B', and which have the
// following import graph edges:
// B imports A
// B' imports A
// A' imports B
// This last edge would be expected to create an error were it not
// for the special type-checking discipline above.
// Cycles of size greater than two are possible. For example:
// compress/bzip2/bzip2_test.go (package bzip2) imports "io/ioutil"
// io/ioutil/tempfile_test.go (package ioutil) imports "regexp"
// regexp/exec_test.go (package regexp) imports "compress/bzip2"
//
//
// Concurrency
// -----------
//
// Let us define the import dependency graph as follows. Each node is a
// list of files passed to (Checker).Files at once. Many of these lists
// are the production code of an importable Go package, so those nodes
// are labelled by the package's path. The remaining nodes are
// ad hoc packages and lists of in-package *_test.go files that augment
// an importable package; those nodes have no label.
//
// The edges of the graph represent import statements appearing within a
// file. An edge connects a node (a list of files) to the node it
// imports, which is importable and thus always labelled.
//
// Loading is controlled by this dependency graph.
//
// To reduce I/O latency, we start loading a package's dependencies
// asynchronously as soon as we've parsed its files and enumerated its
// imports (scanImports). This performs a preorder traversal of the
// import dependency graph.
//
// To exploit hardware parallelism, we type-check unrelated packages in
// parallel, where "unrelated" means not ordered by the partial order of
// the import dependency graph.
//
// We use a concurrency-safe non-blocking cache (importer.imported) to
// record the results of type-checking, whether success or failure. An
// entry is created in this cache by startLoad the first time the
// package is imported. The first goroutine to request an entry becomes
// responsible for completing the task and broadcasting completion to
// subsequent requesters, which block until then.
//
// Type checking occurs in (parallel) postorder: we cannot type-check a
// set of files until we have loaded and type-checked all of their
// immediate dependencies (and thus all of their transitive
// dependencies). If the input were guaranteed free of import cycles,
// this would be trivial: we could simply wait for completion of the
// dependencies and then invoke the typechecker.
//
// But as we saw in the 'go test' section above, some cycles in the
// import graph over packages are actually legal, so long as the
// cycle-forming edge originates in the in-package test files that
// augment the package. This explains why the nodes of the import
// dependency graph are not packages, but lists of files: the unlabelled
// nodes avoid the cycles. Consider packages A and B where B imports A
// and A's in-package tests AT import B. The naively constructed import
// graph over packages would contain a cycle (A+AT) --> B --> (A+AT) but
// the graph over lists of files is AT --> B --> A, where AT is an
// unlabelled node.
//
// Awaiting completion of the dependencies in a cyclic graph would
// deadlock, so we must materialize the import dependency graph (as
// importer.graph) and check whether each import edge forms a cycle. If
// x imports y, and the graph already contains a path from y to x, then
// there is an import cycle, in which case the processing of x must not
// wait for the completion of processing of y.
//
// When the type-checker makes a callback (doImport) to the loader for a
// given import edge, there are two possible cases. In the normal case,
// the dependency has already been completely type-checked; doImport
// does a cache lookup and returns it. In the cyclic case, the entry in
// the cache is still necessarily incomplete, indicating a cycle. We
// perform the cycle check again to obtain the error message, and return
// the error.
//
// The result of using concurrency is about a 2.5x speedup for stdlib_test.

1059
vendor/golang.org/x/tools/go/loader/loader.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

123
vendor/golang.org/x/tools/go/loader/util.go generated vendored Normal file
View File

@@ -0,0 +1,123 @@
// Copyright 2013 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 loader
import (
"go/ast"
"go/build"
"go/parser"
"go/token"
"io"
"os"
"strconv"
"sync"
"golang.org/x/tools/go/buildutil"
)
// We use a counting semaphore to limit
// the number of parallel I/O calls per process.
var ioLimit = make(chan bool, 10)
// parseFiles parses the Go source files within directory dir and
// returns the ASTs of the ones that could be at least partially parsed,
// along with a list of I/O and parse errors encountered.
//
// I/O is done via ctxt, which may specify a virtual file system.
// displayPath is used to transform the filenames attached to the ASTs.
func parseFiles(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, files []string, mode parser.Mode) ([]*ast.File, []error) {
if displayPath == nil {
displayPath = func(path string) string { return path }
}
var wg sync.WaitGroup
n := len(files)
parsed := make([]*ast.File, n)
errors := make([]error, n)
for i, file := range files {
if !buildutil.IsAbsPath(ctxt, file) {
file = buildutil.JoinPath(ctxt, dir, file)
}
wg.Add(1)
go func(i int, file string) {
ioLimit <- true // wait
defer func() {
wg.Done()
<-ioLimit // signal
}()
var rd io.ReadCloser
var err error
if ctxt.OpenFile != nil {
rd, err = ctxt.OpenFile(file)
} else {
rd, err = os.Open(file)
}
if err != nil {
errors[i] = err // open failed
return
}
// ParseFile may return both an AST and an error.
parsed[i], errors[i] = parser.ParseFile(fset, displayPath(file), rd, mode)
rd.Close()
}(i, file)
}
wg.Wait()
// Eliminate nils, preserving order.
var o int
for _, f := range parsed {
if f != nil {
parsed[o] = f
o++
}
}
parsed = parsed[:o]
o = 0
for _, err := range errors {
if err != nil {
errors[o] = err
o++
}
}
errors = errors[:o]
return parsed, errors
}
// scanImports returns the set of all import paths from all
// import specs in the specified files.
func scanImports(files []*ast.File) map[string]bool {
imports := make(map[string]bool)
for _, f := range files {
for _, decl := range f.Decls {
if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT {
for _, spec := range decl.Specs {
spec := spec.(*ast.ImportSpec)
// NB: do not assume the program is well-formed!
path, err := strconv.Unquote(spec.Path.Value)
if err != nil {
continue // quietly ignore the error
}
if path == "C" {
continue // skip pseudopackage
}
imports[path] = true
}
}
}
}
return imports
}
// ---------- Internal helpers ----------
// TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos)
func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
p := int(pos)
base := f.Base()
return base <= p && p < base+f.Size()
}

253
vendor/golang.org/x/tools/go/packages/doc.go generated vendored Normal file
View File

@@ -0,0 +1,253 @@
// Copyright 2018 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 packages loads Go packages for inspection and analysis.
The [Load] function takes as input a list of patterns and returns a
list of [Package] values describing individual packages matched by those
patterns.
A [Config] specifies configuration options, the most important of which is
the [LoadMode], which controls the amount of detail in the loaded packages.
Load passes most patterns directly to the underlying build tool.
The default build tool is the go command.
Its supported patterns are described at
https://pkg.go.dev/cmd/go#hdr-Package_lists_and_patterns.
Other build systems may be supported by providing a "driver";
see [The driver protocol].
All patterns with the prefix "query=", where query is a
non-empty string of letters from [a-z], are reserved and may be
interpreted as query operators.
Two query operators are currently supported: "file" and "pattern".
The query "file=path/to/file.go" matches the package or packages enclosing
the Go source file path/to/file.go. For example "file=~/go/src/fmt/print.go"
might return the packages "fmt" and "fmt [fmt.test]".
The query "pattern=string" causes "string" to be passed directly to
the underlying build tool. In most cases this is unnecessary,
but an application can use Load("pattern=" + x) as an escaping mechanism
to ensure that x is not interpreted as a query operator if it contains '='.
All other query operators are reserved for future use and currently
cause Load to report an error.
The Package struct provides basic information about the package, including
- ID, a unique identifier for the package in the returned set;
- GoFiles, the names of the package's Go source files;
- Imports, a map from source import strings to the Packages they name;
- Types, the type information for the package's exported symbols;
- Syntax, the parsed syntax trees for the package's source code; and
- TypesInfo, the result of a complete type-check of the package syntax trees.
(See the documentation for type Package for the complete list of fields
and more detailed descriptions.)
For example,
Load(nil, "bytes", "unicode...")
returns four Package structs describing the standard library packages
bytes, unicode, unicode/utf16, and unicode/utf8. Note that one pattern
can match multiple packages and that a package might be matched by
multiple patterns: in general it is not possible to determine which
packages correspond to which patterns.
Note that the list returned by Load contains only the packages matched
by the patterns. Their dependencies can be found by walking the import
graph using the Imports fields.
The Load function can be configured by passing a pointer to a Config as
the first argument. A nil Config is equivalent to the zero Config, which
causes Load to run in [LoadFiles] mode, collecting minimal information.
See the documentation for type Config for details.
As noted earlier, the Config.Mode controls the amount of detail
reported about the loaded packages. See the documentation for type LoadMode
for details.
Most tools should pass their command-line arguments (after any flags)
uninterpreted to Load, so that it can interpret them
according to the conventions of the underlying build system.
See the Example function for typical usage.
See also [golang.org/x/tools/go/packages/internal/linecount]
for an example application.
# The driver protocol
Load may be used to load Go packages even in Go projects that use
alternative build systems, by installing an appropriate "driver"
program for the build system and specifying its location in the
GOPACKAGESDRIVER environment variable.
For example,
https://github.com/bazelbuild/rules_go/wiki/Editor-and-tool-integration
explains how to use the driver for Bazel.
The driver program is responsible for interpreting patterns in its
preferred notation and reporting information about the packages that
those patterns identify. Drivers must also support the special "file="
and "pattern=" patterns described above.
The patterns are provided as positional command-line arguments. A
JSON-encoded [DriverRequest] message providing additional information
is written to the driver's standard input. The driver must write a
JSON-encoded [DriverResponse] message to its standard output. (This
message differs from the JSON schema produced by 'go list'.)
The value of the PWD environment variable seen by the driver process
is the preferred name of its working directory. (The working directory
may have other aliases due to symbolic links; see the comment on the
Dir field of [exec.Cmd] for related information.)
When the driver process emits in its response the name of a file
that is a descendant of this directory, it must use an absolute path
that has the value of PWD as a prefix, to ensure that the returned
filenames satisfy the original query.
*/
package packages // import "golang.org/x/tools/go/packages"
/*
Motivation and design considerations
The new package's design solves problems addressed by two existing
packages: go/build, which locates and describes packages, and
golang.org/x/tools/go/loader, which loads, parses and type-checks them.
The go/build.Package structure encodes too much of the 'go build' way
of organizing projects, leaving us in need of a data type that describes a
package of Go source code independent of the underlying build system.
We wanted something that works equally well with go build and vgo, and
also other build systems such as Bazel and Blaze, making it possible to
construct analysis tools that work in all these environments.
Tools such as errcheck and staticcheck were essentially unavailable to
the Go community at Google, and some of Google's internal tools for Go
are unavailable externally.
This new package provides a uniform way to obtain package metadata by
querying each of these build systems, optionally supporting their
preferred command-line notations for packages, so that tools integrate
neatly with users' build environments. The Metadata query function
executes an external query tool appropriate to the current workspace.
Loading packages always returns the complete import graph "all the way down",
even if all you want is information about a single package, because the query
mechanisms of all the build systems we currently support ({go,vgo} list, and
blaze/bazel aspect-based query) cannot provide detailed information
about one package without visiting all its dependencies too, so there is
no additional asymptotic cost to providing transitive information.
(This property might not be true of a hypothetical 5th build system.)
In calls to TypeCheck, all initial packages, and any package that
transitively depends on one of them, must be loaded from source.
Consider A->B->C->D->E: if A,C are initial, A,B,C must be loaded from
source; D may be loaded from export data, and E may not be loaded at all
(though it's possible that D's export data mentions it, so a
types.Package may be created for it and exposed.)
The old loader had a feature to suppress type-checking of function
bodies on a per-package basis, primarily intended to reduce the work of
obtaining type information for imported packages. Now that imports are
satisfied by export data, the optimization no longer seems necessary.
Despite some early attempts, the old loader did not exploit export data,
instead always using the equivalent of WholeProgram mode. This was due
to the complexity of mixing source and export data packages (now
resolved by the upward traversal mentioned above), and because export data
files were nearly always missing or stale. Now that 'go build' supports
caching, all the underlying build systems can guarantee to produce
export data in a reasonable (amortized) time.
Test "main" packages synthesized by the build system are now reported as
first-class packages, avoiding the need for clients (such as go/ssa) to
reinvent this generation logic.
One way in which go/packages is simpler than the old loader is in its
treatment of in-package tests. In-package tests are packages that
consist of all the files of the library under test, plus the test files.
The old loader constructed in-package tests by a two-phase process of
mutation called "augmentation": first it would construct and type check
all the ordinary library packages and type-check the packages that
depend on them; then it would add more (test) files to the package and
type-check again. This two-phase approach had four major problems:
1) in processing the tests, the loader modified the library package,
leaving no way for a client application to see both the test
package and the library package; one would mutate into the other.
2) because test files can declare additional methods on types defined in
the library portion of the package, the dispatch of method calls in
the library portion was affected by the presence of the test files.
This should have been a clue that the packages were logically
different.
3) this model of "augmentation" assumed at most one in-package test
per library package, which is true of projects using 'go build',
but not other build systems.
4) because of the two-phase nature of test processing, all packages that
import the library package had to be processed before augmentation,
forcing a "one-shot" API and preventing the client from calling Load
in several times in sequence as is now possible in WholeProgram mode.
(TypeCheck mode has a similar one-shot restriction for a different reason.)
Early drafts of this package supported "multi-shot" operation.
Although it allowed clients to make a sequence of calls (or concurrent
calls) to Load, building up the graph of Packages incrementally,
it was of marginal value: it complicated the API
(since it allowed some options to vary across calls but not others),
it complicated the implementation,
it cannot be made to work in Types mode, as explained above,
and it was less efficient than making one combined call (when this is possible).
Among the clients we have inspected, none made multiple calls to load
but could not be easily and satisfactorily modified to make only a single call.
However, applications changes may be required.
For example, the ssadump command loads the user-specified packages
and in addition the runtime package. It is tempting to simply append
"runtime" to the user-provided list, but that does not work if the user
specified an ad-hoc package such as [a.go b.go].
Instead, ssadump no longer requests the runtime package,
but seeks it among the dependencies of the user-specified packages,
and emits an error if it is not found.
Questions & Tasks
- Add GOARCH/GOOS?
They are not portable concepts, but could be made portable.
Our goal has been to allow users to express themselves using the conventions
of the underlying build system: if the build system honors GOARCH
during a build and during a metadata query, then so should
applications built atop that query mechanism.
Conversely, if the target architecture of the build is determined by
command-line flags, the application can pass the relevant
flags through to the build system using a command such as:
myapp -query_flag="--cpu=amd64" -query_flag="--os=darwin"
However, this approach is low-level, unwieldy, and non-portable.
GOOS and GOARCH seem important enough to warrant a dedicated option.
- How should we handle partial failures such as a mixture of good and
malformed patterns, existing and non-existent packages, successful and
failed builds, import failures, import cycles, and so on, in a call to
Load?
- Support bazel, blaze, and go1.10 list, not just go1.11 list.
- Handle (and test) various partial success cases, e.g.
a mixture of good packages and:
invalid patterns
nonexistent packages
empty packages
packages with malformed package or import declarations
unreadable files
import cycles
other parse errors
type errors
Make sure we record errors at the correct place in the graph.
- Missing packages among initial arguments are not reported.
Return bogus packages for them, like golist does.
- "undeclared name" errors (for example) are reported out of source file
order. I suspect this is due to the breadth-first resolution now used
by go/types. Is that a bug? Discuss with gri.
*/

153
vendor/golang.org/x/tools/go/packages/external.go generated vendored Normal file
View File

@@ -0,0 +1,153 @@
// Copyright 2018 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 packages
// This file defines the protocol that enables an external "driver"
// tool to supply package metadata in place of 'go list'.
import (
"bytes"
"encoding/json"
"fmt"
"os"
"os/exec"
"slices"
"strings"
)
// DriverRequest defines the schema of a request for package metadata
// from an external driver program. The JSON-encoded DriverRequest
// message is provided to the driver program's standard input. The
// query patterns are provided as command-line arguments.
//
// See the package documentation for an overview.
type DriverRequest struct {
Mode LoadMode `json:"mode"`
// Env specifies the environment the underlying build system should be run in.
Env []string `json:"env"`
// BuildFlags are flags that should be passed to the underlying build system.
BuildFlags []string `json:"build_flags"`
// Tests specifies whether the patterns should also return test packages.
Tests bool `json:"tests"`
// Overlay maps file paths (relative to the driver's working directory)
// to the contents of overlay files (see Config.Overlay).
Overlay map[string][]byte `json:"overlay"`
}
// DriverResponse defines the schema of a response from an external
// driver program, providing the results of a query for package
// metadata. The driver program must write a JSON-encoded
// DriverResponse message to its standard output.
//
// See the package documentation for an overview.
type DriverResponse struct {
// NotHandled is returned if the request can't be handled by the current
// driver. If an external driver returns a response with NotHandled, the
// rest of the DriverResponse is ignored, and go/packages will fallback
// to the next driver. If go/packages is extended in the future to support
// lists of multiple drivers, go/packages will fall back to the next driver.
NotHandled bool
// Compiler and Arch are the arguments pass of types.SizesFor
// to get a types.Sizes to use when type checking.
Compiler string
Arch string
// Roots is the set of package IDs that make up the root packages.
// We have to encode this separately because when we encode a single package
// we cannot know if it is one of the roots as that requires knowledge of the
// graph it is part of.
Roots []string `json:",omitempty"`
// Packages is the full set of packages in the graph.
// The packages are not connected into a graph.
// The Imports if populated will be stubs that only have their ID set.
// Imports will be connected and then type and syntax information added in a
// later pass (see refine).
Packages []*Package
// GoVersion is the minor version number used by the driver
// (e.g. the go command on the PATH) when selecting .go files.
// Zero means unknown.
GoVersion int
}
// driver is the type for functions that query the build system for the
// packages named by the patterns.
type driver func(cfg *Config, patterns []string) (*DriverResponse, error)
// findExternalDriver returns the file path of a tool that supplies
// the build system package structure, or "" if not found.
// If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
// value, otherwise it searches for a binary named gopackagesdriver on the PATH.
func findExternalDriver(cfg *Config) driver {
const toolPrefix = "GOPACKAGESDRIVER="
tool := ""
for _, env := range cfg.Env {
if val, ok := strings.CutPrefix(env, toolPrefix); ok {
tool = val
}
}
if tool != "" && tool == "off" {
return nil
}
if tool == "" {
var err error
tool, err = exec.LookPath("gopackagesdriver")
if err != nil {
return nil
}
}
return func(cfg *Config, patterns []string) (*DriverResponse, error) {
req, err := json.Marshal(DriverRequest{
Mode: cfg.Mode,
Env: cfg.Env,
BuildFlags: cfg.BuildFlags,
Tests: cfg.Tests,
Overlay: cfg.Overlay,
})
if err != nil {
return nil, fmt.Errorf("failed to encode message to driver tool: %v", err)
}
buf := new(bytes.Buffer)
stderr := new(bytes.Buffer)
cmd := exec.CommandContext(cfg.Context, tool, patterns...)
cmd.Dir = cfg.Dir
// The cwd gets resolved to the real path. On Darwin, where
// /tmp is a symlink, this breaks anything that expects the
// working directory to keep the original path, including the
// go command when dealing with modules.
//
// os.Getwd stdlib has a special feature where if the
// cwd and the PWD are the same node then it trusts
// the PWD, so by setting it in the env for the child
// process we fix up all the paths returned by the go
// command.
//
// (See similar trick in Invocation.run in ../../internal/gocommand/invoke.go)
cmd.Env = append(slices.Clip(cfg.Env), "PWD="+cfg.Dir)
cmd.Stdin = bytes.NewReader(req)
cmd.Stdout = buf
cmd.Stderr = stderr
if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
}
if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" {
fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr)
}
var response DriverResponse
if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
return nil, err
}
return &response, nil
}
}

1086
vendor/golang.org/x/tools/go/packages/golist.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,83 @@
// Copyright 2018 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 packages
import (
"encoding/json"
"path/filepath"
"golang.org/x/tools/internal/gocommand"
)
// determineRootDirs returns a mapping from absolute directories that could
// contain code to their corresponding import path prefixes.
func (state *golistState) determineRootDirs() (map[string]string, error) {
env, err := state.getEnv()
if err != nil {
return nil, err
}
if env["GOMOD"] != "" {
state.rootsOnce.Do(func() {
state.rootDirs, state.rootDirsError = state.determineRootDirsModules()
})
} else {
state.rootsOnce.Do(func() {
state.rootDirs, state.rootDirsError = state.determineRootDirsGOPATH()
})
}
return state.rootDirs, state.rootDirsError
}
func (state *golistState) determineRootDirsModules() (map[string]string, error) {
// List all of the modules--the first will be the directory for the main
// module. Any replaced modules will also need to be treated as roots.
// Editing files in the module cache isn't a great idea, so we don't
// plan to ever support that.
out, err := state.invokeGo("list", "-m", "-json", "all")
if err != nil {
// 'go list all' will fail if we're outside of a module and
// GO111MODULE=on. Try falling back without 'all'.
var innerErr error
out, innerErr = state.invokeGo("list", "-m", "-json")
if innerErr != nil {
return nil, err
}
}
roots := map[string]string{}
modules := map[string]string{}
var i int
for dec := json.NewDecoder(out); dec.More(); {
mod := new(gocommand.ModuleJSON)
if err := dec.Decode(mod); err != nil {
return nil, err
}
if mod.Dir != "" && mod.Path != "" {
// This is a valid module; add it to the map.
absDir, err := state.cfg.abs(mod.Dir)
if err != nil {
return nil, err
}
modules[absDir] = mod.Path
// The first result is the main module.
if i == 0 || mod.Replace != nil && mod.Replace.Path != "" {
roots[absDir] = mod.Path
}
}
i++
}
return roots, nil
}
func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) {
m := map[string]string{}
for _, dir := range filepath.SplitList(state.mustGetEnv()["GOPATH"]) {
absDir, err := filepath.Abs(dir)
if err != nil {
return nil, err
}
m[filepath.Join(absDir, "src")] = ""
}
return m, nil
}

View File

@@ -0,0 +1,56 @@
// Copyright 2019 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 packages
import (
"fmt"
"strings"
)
var modes = [...]struct {
mode LoadMode
name string
}{
{NeedName, "NeedName"},
{NeedFiles, "NeedFiles"},
{NeedCompiledGoFiles, "NeedCompiledGoFiles"},
{NeedImports, "NeedImports"},
{NeedDeps, "NeedDeps"},
{NeedExportFile, "NeedExportFile"},
{NeedTypes, "NeedTypes"},
{NeedSyntax, "NeedSyntax"},
{NeedTypesInfo, "NeedTypesInfo"},
{NeedTypesSizes, "NeedTypesSizes"},
{NeedForTest, "NeedForTest"},
{NeedModule, "NeedModule"},
{NeedEmbedFiles, "NeedEmbedFiles"},
{NeedEmbedPatterns, "NeedEmbedPatterns"},
{NeedTarget, "NeedTarget"},
}
func (mode LoadMode) String() string {
if mode == 0 {
return "LoadMode(0)"
}
var out []string
// named bits
for _, item := range modes {
if (mode & item.mode) != 0 {
mode ^= item.mode
out = append(out, item.name)
}
}
// unnamed residue
if mode != 0 {
if out == nil {
return fmt.Sprintf("LoadMode(%#x)", int(mode))
}
out = append(out, fmt.Sprintf("%#x", int(mode)))
}
if len(out) == 1 {
return out[0]
}
return "(" + strings.Join(out, "|") + ")"
}

1568
vendor/golang.org/x/tools/go/packages/packages.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

133
vendor/golang.org/x/tools/go/packages/visit.go generated vendored Normal file
View File

@@ -0,0 +1,133 @@
// Copyright 2018 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 packages
import (
"cmp"
"fmt"
"iter"
"os"
"slices"
)
// Visit visits all the packages in the import graph whose roots are
// pkgs, calling the optional pre function the first time each package
// is encountered (preorder), and the optional post function after a
// package's dependencies have been visited (postorder).
// The boolean result of pre(pkg) determines whether
// the imports of package pkg are visited.
//
// Example:
//
// pkgs, err := Load(...)
// if err != nil { ... }
// Visit(pkgs, nil, func(pkg *Package) {
// log.Println(pkg)
// })
//
// In most cases, it is more convenient to use [Postorder]:
//
// for pkg := range Postorder(pkgs) {
// log.Println(pkg)
// }
func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) {
seen := make(map[*Package]bool)
var visit func(*Package)
visit = func(pkg *Package) {
if !seen[pkg] {
seen[pkg] = true
if pre == nil || pre(pkg) {
for _, imp := range sorted(pkg.Imports) { // for determinism
visit(imp)
}
}
if post != nil {
post(pkg)
}
}
}
for _, pkg := range pkgs {
visit(pkg)
}
}
// PrintErrors prints to os.Stderr the accumulated errors of all
// packages in the import graph rooted at pkgs, dependencies first.
// PrintErrors returns the number of errors printed.
func PrintErrors(pkgs []*Package) int {
var n int
errModules := make(map[*Module]bool)
for pkg := range Postorder(pkgs) {
for _, err := range pkg.Errors {
fmt.Fprintln(os.Stderr, err)
n++
}
// Print pkg.Module.Error once if present.
mod := pkg.Module
if mod != nil && mod.Error != nil && !errModules[mod] {
errModules[mod] = true
fmt.Fprintln(os.Stderr, mod.Error.Err)
n++
}
}
return n
}
// Postorder returns an iterator over the packages in
// the import graph whose roots are pkg.
// Packages are enumerated in dependencies-first order.
func Postorder(pkgs []*Package) iter.Seq[*Package] {
return func(yield func(*Package) bool) {
seen := make(map[*Package]bool)
var visit func(*Package) bool
visit = func(pkg *Package) bool {
if !seen[pkg] {
seen[pkg] = true
for _, imp := range sorted(pkg.Imports) { // for determinism
if !visit(imp) {
return false
}
}
if !yield(pkg) {
return false
}
}
return true
}
for _, pkg := range pkgs {
if !visit(pkg) {
break
}
}
}
}
// -- copied from golang.org.x/tools/gopls/internal/util/moremaps --
// sorted returns an iterator over the entries of m in key order.
func sorted[M ~map[K]V, K cmp.Ordered, V any](m M) iter.Seq2[K, V] {
// TODO(adonovan): use maps.Sorted if proposal #68598 is accepted.
return func(yield func(K, V) bool) {
keys := keySlice(m)
slices.Sort(keys)
for _, k := range keys {
if !yield(k, m[k]) {
break
}
}
}
}
// KeySlice returns the keys of the map M, like slices.Collect(maps.Keys(m)).
func keySlice[M ~map[K]V, K comparable, V any](m M) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}

16
vendor/golang.org/x/tools/go/ssa/TODO generated vendored Normal file
View File

@@ -0,0 +1,16 @@
-*- text -*-
SSA Generics to-do list
===========================
DOCUMENTATION:
- Read me for internals
TYPE PARAMETERIZED GENERIC FUNCTIONS:
- sanity.go updates.
- Check source functions going to generics.
- Tests, tests, tests...
USAGE:
- Back fill users for handling ssa.InstantiateGenerics being off.

113
vendor/golang.org/x/tools/go/ssa/block.go generated vendored Normal file
View File

@@ -0,0 +1,113 @@
// Copyright 2022 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 ssa
import "fmt"
// This file implements the BasicBlock type.
// addEdge adds a control-flow graph edge from from to to.
func addEdge(from, to *BasicBlock) {
from.Succs = append(from.Succs, to)
to.Preds = append(to.Preds, from)
}
// Parent returns the function that contains block b.
func (b *BasicBlock) Parent() *Function { return b.parent }
// String returns a human-readable label of this block.
// It is not guaranteed unique within the function.
func (b *BasicBlock) String() string {
return fmt.Sprintf("%d", b.Index)
}
// emit appends an instruction to the current basic block.
// If the instruction defines a Value, it is returned.
func (b *BasicBlock) emit(i Instruction) Value {
i.setBlock(b)
b.Instrs = append(b.Instrs, i)
v, _ := i.(Value)
return v
}
// predIndex returns the i such that b.Preds[i] == c or panics if
// there is none.
func (b *BasicBlock) predIndex(c *BasicBlock) int {
for i, pred := range b.Preds {
if pred == c {
return i
}
}
panic(fmt.Sprintf("no edge %s -> %s", c, b))
}
// hasPhi returns true if b.Instrs contains φ-nodes.
func (b *BasicBlock) hasPhi() bool {
_, ok := b.Instrs[0].(*Phi)
return ok
}
// phis returns the prefix of b.Instrs containing all the block's φ-nodes.
func (b *BasicBlock) phis() []Instruction {
for i, instr := range b.Instrs {
if _, ok := instr.(*Phi); !ok {
return b.Instrs[:i]
}
}
return nil // unreachable in well-formed blocks
}
// replacePred replaces all occurrences of p in b's predecessor list with q.
// Ordinarily there should be at most one.
func (b *BasicBlock) replacePred(p, q *BasicBlock) {
for i, pred := range b.Preds {
if pred == p {
b.Preds[i] = q
}
}
}
// replaceSucc replaces all occurrences of p in b's successor list with q.
// Ordinarily there should be at most one.
func (b *BasicBlock) replaceSucc(p, q *BasicBlock) {
for i, succ := range b.Succs {
if succ == p {
b.Succs[i] = q
}
}
}
// removePred removes all occurrences of p in b's
// predecessor list and φ-nodes.
// Ordinarily there should be at most one.
func (b *BasicBlock) removePred(p *BasicBlock) {
phis := b.phis()
// We must preserve edge order for φ-nodes.
j := 0
for i, pred := range b.Preds {
if pred != p {
b.Preds[j] = b.Preds[i]
// Strike out φ-edge too.
for _, instr := range phis {
phi := instr.(*Phi)
phi.Edges[j] = phi.Edges[i]
}
j++
}
}
// Nil out b.Preds[j:] and φ-edges[j:] to aid GC.
for i := j; i < len(b.Preds); i++ {
b.Preds[i] = nil
for _, instr := range phis {
instr.(*Phi).Edges[i] = nil
}
}
b.Preds = b.Preds[:j]
for _, instr := range phis {
phi := instr.(*Phi)
phi.Edges = phi.Edges[:j]
}
}

183
vendor/golang.org/x/tools/go/ssa/blockopt.go generated vendored Normal file
View File

@@ -0,0 +1,183 @@
// Copyright 2013 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 ssa
// Simple block optimizations to simplify the control flow graph.
// TODO(adonovan): opt: instead of creating several "unreachable" blocks
// per function in the Builder, reuse a single one (e.g. at Blocks[1])
// to reduce garbage.
import (
"fmt"
"os"
)
// If true, perform sanity checking and show progress at each
// successive iteration of optimizeBlocks. Very verbose.
const debugBlockOpt = false
// markReachable sets Index=-1 for all blocks reachable from b.
func markReachable(b *BasicBlock) {
b.Index = -1
for _, succ := range b.Succs {
if succ.Index == 0 {
markReachable(succ)
}
}
}
// deleteUnreachableBlocks marks all reachable blocks of f and
// eliminates (nils) all others, including possibly cyclic subgraphs.
func deleteUnreachableBlocks(f *Function) {
const white, black = 0, -1
// We borrow b.Index temporarily as the mark bit.
for _, b := range f.Blocks {
b.Index = white
}
markReachable(f.Blocks[0])
if f.Recover != nil {
markReachable(f.Recover)
}
for i, b := range f.Blocks {
if b.Index == white {
for _, c := range b.Succs {
if c.Index == black {
c.removePred(b) // delete white->black edge
}
}
if debugBlockOpt {
fmt.Fprintln(os.Stderr, "unreachable", b)
}
f.Blocks[i] = nil // delete b
}
}
f.removeNilBlocks()
}
// jumpThreading attempts to apply simple jump-threading to block b,
// in which a->b->c become a->c if b is just a Jump.
// The result is true if the optimization was applied.
func jumpThreading(f *Function, b *BasicBlock) bool {
if b.Index == 0 {
return false // don't apply to entry block
}
if b.Instrs == nil {
return false
}
if _, ok := b.Instrs[0].(*Jump); !ok {
return false // not just a jump
}
c := b.Succs[0]
if c == b {
return false // don't apply to degenerate jump-to-self.
}
if c.hasPhi() {
return false // not sound without more effort
}
for j, a := range b.Preds {
a.replaceSucc(b, c)
// If a now has two edges to c, replace its degenerate If by Jump.
if len(a.Succs) == 2 && a.Succs[0] == c && a.Succs[1] == c {
jump := new(Jump)
jump.setBlock(a)
a.Instrs[len(a.Instrs)-1] = jump
a.Succs = a.Succs[:1]
c.removePred(b)
} else {
if j == 0 {
c.replacePred(b, a)
} else {
c.Preds = append(c.Preds, a)
}
}
if debugBlockOpt {
fmt.Fprintln(os.Stderr, "jumpThreading", a, b, c)
}
}
f.Blocks[b.Index] = nil // delete b
return true
}
// fuseBlocks attempts to apply the block fusion optimization to block
// a, in which a->b becomes ab if len(a.Succs)==len(b.Preds)==1.
// The result is true if the optimization was applied.
func fuseBlocks(f *Function, a *BasicBlock) bool {
if len(a.Succs) != 1 {
return false
}
b := a.Succs[0]
if len(b.Preds) != 1 {
return false
}
// Degenerate &&/|| ops may result in a straight-line CFG
// containing φ-nodes. (Ideally we'd replace such them with
// their sole operand but that requires Referrers, built later.)
if b.hasPhi() {
return false // not sound without further effort
}
// Eliminate jump at end of A, then copy all of B across.
a.Instrs = append(a.Instrs[:len(a.Instrs)-1], b.Instrs...)
for _, instr := range b.Instrs {
instr.setBlock(a)
}
// A inherits B's successors
a.Succs = append(a.succs2[:0], b.Succs...)
// Fix up Preds links of all successors of B.
for _, c := range b.Succs {
c.replacePred(b, a)
}
if debugBlockOpt {
fmt.Fprintln(os.Stderr, "fuseBlocks", a, b)
}
f.Blocks[b.Index] = nil // delete b
return true
}
// optimizeBlocks() performs some simple block optimizations on a
// completed function: dead block elimination, block fusion, jump
// threading.
func optimizeBlocks(f *Function) {
deleteUnreachableBlocks(f)
// Loop until no further progress.
changed := true
for changed {
changed = false
if debugBlockOpt {
f.WriteTo(os.Stderr)
mustSanityCheck(f, nil)
}
for _, b := range f.Blocks {
// f.Blocks will temporarily contain nils to indicate
// deleted blocks; we remove them at the end.
if b == nil {
continue
}
// Fuse blocks. b->c becomes bc.
if fuseBlocks(f, b) {
changed = true
}
// a->b->c becomes a->c if b contains only a Jump.
if jumpThreading(f, b) {
changed = true
continue // (b was disconnected)
}
}
}
f.removeNilBlocks()
}

3292
vendor/golang.org/x/tools/go/ssa/builder.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

193
vendor/golang.org/x/tools/go/ssa/const.go generated vendored Normal file
View File

@@ -0,0 +1,193 @@
// Copyright 2013 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 ssa
// This file defines the Const SSA value type.
import (
"fmt"
"go/constant"
"go/token"
"go/types"
"strconv"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/typesinternal"
)
// NewConst returns a new constant of the specified value and type.
// val must be valid according to the specification of Const.Value.
func NewConst(val constant.Value, typ types.Type) *Const {
if val == nil {
switch soleTypeKind(typ) {
case types.IsBoolean:
val = constant.MakeBool(false)
case types.IsInteger:
val = constant.MakeInt64(0)
case types.IsString:
val = constant.MakeString("")
}
}
return &Const{typ, val}
}
// soleTypeKind returns a BasicInfo for which constant.Value can
// represent all zero values for the types in the type set.
//
// types.IsBoolean for false is a representative.
// types.IsInteger for 0
// types.IsString for ""
// 0 otherwise.
func soleTypeKind(typ types.Type) types.BasicInfo {
// State records the set of possible zero values (false, 0, "").
// Candidates (perhaps all) are eliminated during the type-set
// iteration, which executes at least once.
state := types.IsBoolean | types.IsInteger | types.IsString
underIs(typ, func(ut types.Type) bool {
var c types.BasicInfo
if t, ok := ut.(*types.Basic); ok {
c = t.Info()
}
if c&types.IsNumeric != 0 { // int/float/complex
c = types.IsInteger
}
state = state & c
return state != 0
})
return state
}
// intConst returns an 'int' constant that evaluates to i.
// (i is an int64 in case the host is narrower than the target.)
func intConst(i int64) *Const {
return NewConst(constant.MakeInt64(i), tInt)
}
// stringConst returns a 'string' constant that evaluates to s.
func stringConst(s string) *Const {
return NewConst(constant.MakeString(s), tString)
}
// zeroConst returns a new "zero" constant of the specified type.
func zeroConst(t types.Type) *Const {
return NewConst(nil, t)
}
func (c *Const) RelString(from *types.Package) string {
var s string
if c.Value == nil {
s, _ = typesinternal.ZeroString(c.typ, types.RelativeTo(from))
} else if c.Value.Kind() == constant.String {
s = constant.StringVal(c.Value)
const max = 20
// TODO(adonovan): don't cut a rune in half.
if len(s) > max {
s = s[:max-3] + "..." // abbreviate
}
s = strconv.Quote(s)
} else {
s = c.Value.String()
}
return s + ":" + relType(c.Type(), from)
}
func (c *Const) Name() string {
return c.RelString(nil)
}
func (c *Const) String() string {
return c.Name()
}
func (c *Const) Type() types.Type {
return c.typ
}
func (c *Const) Referrers() *[]Instruction {
return nil
}
func (c *Const) Parent() *Function { return nil }
func (c *Const) Pos() token.Pos {
return token.NoPos
}
// IsNil returns true if this constant is a nil value of
// a nillable reference type (pointer, slice, channel, map, or function),
// a basic interface type, or
// a type parameter all of whose possible instantiations are themselves nillable.
func (c *Const) IsNil() bool {
return c.Value == nil && nillable(c.typ)
}
// nillable reports whether *new(T) == nil is legal for type T.
func nillable(t types.Type) bool {
if typeparams.IsTypeParam(t) {
return underIs(t, func(u types.Type) bool {
// empty type set (u==nil) => any underlying types => not nillable
return u != nil && nillable(u)
})
}
switch t.Underlying().(type) {
case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature:
return true
case *types.Interface:
return true // basic interface.
default:
return false
}
}
// TODO(adonovan): move everything below into golang.org/x/tools/go/ssa/interp.
// Int64 returns the numeric value of this constant truncated to fit
// a signed 64-bit integer.
func (c *Const) Int64() int64 {
switch x := constant.ToInt(c.Value); x.Kind() {
case constant.Int:
if i, ok := constant.Int64Val(x); ok {
return i
}
return 0
case constant.Float:
f, _ := constant.Float64Val(x)
return int64(f)
}
panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
}
// Uint64 returns the numeric value of this constant truncated to fit
// an unsigned 64-bit integer.
func (c *Const) Uint64() uint64 {
switch x := constant.ToInt(c.Value); x.Kind() {
case constant.Int:
if u, ok := constant.Uint64Val(x); ok {
return u
}
return 0
case constant.Float:
f, _ := constant.Float64Val(x)
return uint64(f)
}
panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
}
// Float64 returns the numeric value of this constant truncated to fit
// a float64.
func (c *Const) Float64() float64 {
x := constant.ToFloat(c.Value) // (c.Value == nil) => x.Kind() == Unknown
f, _ := constant.Float64Val(x)
return f
}
// Complex128 returns the complex value of this constant truncated to
// fit a complex128.
func (c *Const) Complex128() complex128 {
x := constant.ToComplex(c.Value) // (c.Value == nil) => x.Kind() == Unknown
re, _ := constant.Float64Val(constant.Real(x))
im, _ := constant.Float64Val(constant.Imag(x))
return complex(re, im)
}

325
vendor/golang.org/x/tools/go/ssa/create.go generated vendored Normal file
View File

@@ -0,0 +1,325 @@
// Copyright 2013 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 ssa
// This file implements the CREATE phase of SSA construction.
// See builder.go for explanation.
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"os"
"sync"
"golang.org/x/tools/internal/versions"
)
// NewProgram returns a new SSA Program.
//
// mode controls diagnostics and checking during SSA construction.
//
// To construct an SSA program:
//
// - Call NewProgram to create an empty Program.
// - Call CreatePackage providing typed syntax for each package
// you want to build, and call it with types but not
// syntax for each of those package's direct dependencies.
// - Call [Package.Build] on each syntax package you wish to build,
// or [Program.Build] to build all of them.
//
// See the Example tests for simple examples.
func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
return &Program{
Fset: fset,
imported: make(map[string]*Package),
packages: make(map[*types.Package]*Package),
mode: mode,
canon: newCanonizer(),
ctxt: types.NewContext(),
}
}
// memberFromObject populates package pkg with a member for the
// typechecker object obj.
//
// For objects from Go source code, syntax is the associated syntax
// tree (for funcs and vars only) and goversion defines the
// appropriate interpretation; they will be used during the build
// phase.
func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node, goversion string) {
name := obj.Name()
switch obj := obj.(type) {
case *types.Builtin:
if pkg.Pkg != types.Unsafe {
panic("unexpected builtin object: " + obj.String())
}
case *types.TypeName:
if name != "_" {
pkg.Members[name] = &Type{
object: obj,
pkg: pkg,
}
}
case *types.Const:
c := &NamedConst{
object: obj,
Value: NewConst(obj.Val(), obj.Type()),
pkg: pkg,
}
pkg.objects[obj] = c
if name != "_" {
pkg.Members[name] = c
}
case *types.Var:
g := &Global{
Pkg: pkg,
name: name,
object: obj,
typ: types.NewPointer(obj.Type()), // address
pos: obj.Pos(),
}
pkg.objects[obj] = g
if name != "_" {
pkg.Members[name] = g
}
case *types.Func:
sig := obj.Type().(*types.Signature)
if sig.Recv() == nil && name == "init" {
pkg.ninit++
name = fmt.Sprintf("init#%d", pkg.ninit)
}
fn := createFunction(pkg.Prog, obj, name, syntax, pkg.info, goversion)
fn.Pkg = pkg
pkg.created = append(pkg.created, fn)
pkg.objects[obj] = fn
if name != "_" && sig.Recv() == nil {
pkg.Members[name] = fn // package-level function
}
default: // (incl. *types.Package)
panic("unexpected Object type: " + obj.String())
}
}
// createFunction creates a function or method. It supports both
// CreatePackage (with or without syntax) and the on-demand creation
// of methods in non-created packages based on their types.Func.
func createFunction(prog *Program, obj *types.Func, name string, syntax ast.Node, info *types.Info, goversion string) *Function {
sig := obj.Type().(*types.Signature)
// Collect type parameters.
var tparams *types.TypeParamList
if rtparams := sig.RecvTypeParams(); rtparams.Len() > 0 {
tparams = rtparams // method of generic type
} else if sigparams := sig.TypeParams(); sigparams.Len() > 0 {
tparams = sigparams // generic function
}
/* declared function/method (from syntax or export data) */
fn := &Function{
name: name,
object: obj,
Signature: sig,
build: (*builder).buildFromSyntax,
syntax: syntax,
info: info,
goversion: goversion,
pos: obj.Pos(),
Pkg: nil, // may be set by caller
Prog: prog,
typeparams: tparams,
}
if fn.syntax == nil {
fn.Synthetic = "from type information"
fn.build = (*builder).buildParamsOnly
}
if tparams.Len() > 0 {
fn.generic = new(generic)
}
return fn
}
// membersFromDecl populates package pkg with members for each
// typechecker object (var, func, const or type) associated with the
// specified decl.
func membersFromDecl(pkg *Package, decl ast.Decl, goversion string) {
switch decl := decl.(type) {
case *ast.GenDecl: // import, const, type or var
switch decl.Tok {
case token.CONST:
for _, spec := range decl.Specs {
for _, id := range spec.(*ast.ValueSpec).Names {
memberFromObject(pkg, pkg.info.Defs[id], nil, "")
}
}
case token.VAR:
for _, spec := range decl.Specs {
for _, rhs := range spec.(*ast.ValueSpec).Values {
pkg.initVersion[rhs] = goversion
}
for _, id := range spec.(*ast.ValueSpec).Names {
memberFromObject(pkg, pkg.info.Defs[id], spec, goversion)
}
}
case token.TYPE:
for _, spec := range decl.Specs {
id := spec.(*ast.TypeSpec).Name
memberFromObject(pkg, pkg.info.Defs[id], nil, "")
}
}
case *ast.FuncDecl:
id := decl.Name
memberFromObject(pkg, pkg.info.Defs[id], decl, goversion)
}
}
// CreatePackage creates and returns an SSA Package from the
// specified type-checked, error-free file ASTs, and populates its
// Members mapping.
//
// importable determines whether this package should be returned by a
// subsequent call to ImportedPackage(pkg.Path()).
//
// The real work of building SSA form for each function is not done
// until a subsequent call to Package.Build.
func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
if pkg == nil {
panic("nil pkg") // otherwise pkg.Scope below returns types.Universe!
}
p := &Package{
Prog: prog,
Members: make(map[string]Member),
objects: make(map[types.Object]Member),
Pkg: pkg,
syntax: info != nil,
// transient values (cleared after Package.Build)
info: info,
files: files,
initVersion: make(map[ast.Expr]string),
}
/* synthesized package initializer */
p.init = &Function{
name: "init",
Signature: new(types.Signature),
Synthetic: "package initializer",
Pkg: p,
Prog: prog,
build: (*builder).buildPackageInit,
info: p.info,
goversion: "", // See Package.build for details.
}
p.Members[p.init.name] = p.init
p.created = append(p.created, p.init)
// Allocate all package members: vars, funcs, consts and types.
if len(files) > 0 {
// Go source package.
for _, file := range files {
goversion := versions.Lang(versions.FileVersion(p.info, file))
for _, decl := range file.Decls {
membersFromDecl(p, decl, goversion)
}
}
} else {
// GC-compiled binary package (or "unsafe")
// No code.
// No position information.
scope := p.Pkg.Scope()
for _, name := range scope.Names() {
obj := scope.Lookup(name)
memberFromObject(p, obj, nil, "")
if obj, ok := obj.(*types.TypeName); ok {
// No Unalias: aliases should not duplicate methods.
if named, ok := obj.Type().(*types.Named); ok {
for i, n := 0, named.NumMethods(); i < n; i++ {
memberFromObject(p, named.Method(i), nil, "")
}
}
}
}
}
if prog.mode&BareInits == 0 {
// Add initializer guard variable.
initguard := &Global{
Pkg: p,
name: "init$guard",
typ: types.NewPointer(tBool),
}
p.Members[initguard.Name()] = initguard
}
if prog.mode&GlobalDebug != 0 {
p.SetDebugMode(true)
}
if prog.mode&PrintPackages != 0 {
printMu.Lock()
p.WriteTo(os.Stdout)
printMu.Unlock()
}
if importable {
prog.imported[p.Pkg.Path()] = p
}
prog.packages[p.Pkg] = p
return p
}
// printMu serializes printing of Packages/Functions to stdout.
var printMu sync.Mutex
// AllPackages returns a new slice containing all packages created by
// prog.CreatePackage in unspecified order.
func (prog *Program) AllPackages() []*Package {
pkgs := make([]*Package, 0, len(prog.packages))
for _, pkg := range prog.packages {
pkgs = append(pkgs, pkg)
}
return pkgs
}
// ImportedPackage returns the importable Package whose PkgPath
// is path, or nil if no such Package has been created.
//
// A parameter to CreatePackage determines whether a package should be
// considered importable. For example, no import declaration can resolve
// to the ad-hoc main package created by 'go build foo.go'.
//
// TODO(adonovan): rethink this function and the "importable" concept;
// most packages are importable. This function assumes that all
// types.Package.Path values are unique within the ssa.Program, which is
// false---yet this function remains very convenient.
// Clients should use (*Program).Package instead where possible.
// SSA doesn't really need a string-keyed map of packages.
//
// Furthermore, the graph of packages may contain multiple variants
// (e.g. "p" vs "p as compiled for q.test"), and each has a different
// view of its dependencies.
func (prog *Program) ImportedPackage(path string) *Package {
return prog.imported[path]
}
// SetNoReturn sets the predicate used when building the ssa.Program
// prog that reports whether a given function cannot return.
// This may be used to prune spurious control flow edges
// after (e.g.) log.Fatal, improving the precision of analyses.
//
// A typical implementation is the [ctrlflow.CFGs.NoReturn] method from
// [golang.org/x/tools/go/analysis/passes/ctrlflow].
func (prog *Program) SetNoReturn(noReturn func(*types.Func) bool) {
prog.noReturn = noReturn
}

122
vendor/golang.org/x/tools/go/ssa/doc.go generated vendored Normal file
View File

@@ -0,0 +1,122 @@
// Copyright 2013 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 ssa defines a representation of the elements of Go programs
// (packages, types, functions, variables and constants) using a
// static single-assignment (SSA) form intermediate representation
// (IR) for the bodies of functions.
//
// For an introduction to SSA form, see
// http://en.wikipedia.org/wiki/Static_single_assignment_form.
// This page provides a broader reading list:
// http://www.dcs.gla.ac.uk/~jsinger/ssa.html.
//
// The level of abstraction of the SSA form is intentionally close to
// the source language to facilitate construction of source analysis
// tools. It is not intended for machine code generation.
//
// All looping, branching and switching constructs are replaced with
// unstructured control flow. Higher-level control flow constructs
// such as multi-way branch can be reconstructed as needed; see
// [golang.org/x/tools/go/ssa/ssautil.Switches] for an example.
//
// The simplest way to create the SSA representation of a package is
// to load typed syntax trees using [golang.org/x/tools/go/packages], then
// invoke the [golang.org/x/tools/go/ssa/ssautil.Packages] helper function.
// (See the package-level Examples named LoadPackages and LoadWholeProgram.)
// The resulting [ssa.Program] contains all the packages and their
// members, but SSA code is not created for function bodies until a
// subsequent call to [Package.Build] or [Program.Build].
//
// The builder initially builds a naive SSA form in which all local
// variables are addresses of stack locations with explicit loads and
// stores. Registerisation of eligible locals and φ-node insertion
// using dominance and dataflow are then performed as a second pass
// called "lifting" to improve the accuracy and performance of
// subsequent analyses; this pass can be skipped by setting the
// NaiveForm builder flag.
//
// The primary interfaces of this package are:
//
// - [Member]: a named member of a Go package.
// - [Value]: an expression that yields a value.
// - [Instruction]: a statement that consumes values and performs computation.
// - [Node]: a [Value] or [Instruction] (emphasizing its membership in the SSA value graph)
//
// A computation that yields a result implements both the [Value] and
// [Instruction] interfaces. The following table shows for each
// concrete type which of these interfaces it implements.
//
// Value? Instruction? Member?
// *Alloc ✔ ✔
// *BinOp ✔ ✔
// *Builtin ✔
// *Call ✔ ✔
// *ChangeInterface ✔ ✔
// *ChangeType ✔ ✔
// *Const ✔
// *Convert ✔ ✔
// *DebugRef ✔
// *Defer ✔
// *Extract ✔ ✔
// *Field ✔ ✔
// *FieldAddr ✔ ✔
// *FreeVar ✔
// *Function ✔ ✔ (func)
// *Global ✔ ✔ (var)
// *Go ✔
// *If ✔
// *Index ✔ ✔
// *IndexAddr ✔ ✔
// *Jump ✔
// *Lookup ✔ ✔
// *MakeChan ✔ ✔
// *MakeClosure ✔ ✔
// *MakeInterface ✔ ✔
// *MakeMap ✔ ✔
// *MakeSlice ✔ ✔
// *MapUpdate ✔
// *MultiConvert ✔ ✔
// *NamedConst ✔ (const)
// *Next ✔ ✔
// *Panic ✔
// *Parameter ✔
// *Phi ✔ ✔
// *Range ✔ ✔
// *Return ✔
// *RunDefers ✔
// *Select ✔ ✔
// *Send ✔
// *Slice ✔ ✔
// *SliceToArrayPointer ✔ ✔
// *Store ✔
// *Type ✔ (type)
// *TypeAssert ✔ ✔
// *UnOp ✔ ✔
//
// Other key types in this package include: [Program], [Package], [Function]
// and [BasicBlock].
//
// The program representation constructed by this package is fully
// resolved internally, i.e. it does not rely on the names of Values,
// Packages, Functions, Types or BasicBlocks for the correct
// interpretation of the program. Only the identities of objects and
// the topology of the SSA and type graphs are semantically
// significant. (There is one exception: [types.Id] values, which identify field
// and method names, contain strings.) Avoidance of name-based
// operations simplifies the implementation of subsequent passes and
// can make them very efficient. Many objects are nonetheless named
// to aid in debugging, but it is not essential that the names be
// either accurate or unambiguous. The public API exposes a number of
// name-based maps for client convenience.
//
// The [golang.org/x/tools/go/ssa/ssautil] package provides various
// helper functions, for example to simplify loading a Go program into
// SSA form.
//
// TODO(adonovan): write a how-to document for all the various cases
// of trying to determine corresponding elements across the four
// domains of source locations, ast.Nodes, types.Objects,
// ssa.Values/Instructions.
package ssa // import "golang.org/x/tools/go/ssa"

342
vendor/golang.org/x/tools/go/ssa/dom.go generated vendored Normal file
View File

@@ -0,0 +1,342 @@
// Copyright 2013 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 ssa
// This file defines algorithms related to dominance.
// Dominator tree construction ----------------------------------------
//
// We use the algorithm described in Lengauer & Tarjan. 1979. A fast
// algorithm for finding dominators in a flowgraph.
// http://doi.acm.org/10.1145/357062.357071
//
// We also apply the optimizations to SLT described in Georgiadis et
// al, Finding Dominators in Practice, JGAA 2006,
// http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
// to avoid the need for buckets of size > 1.
import (
"bytes"
"fmt"
"math/big"
"os"
"slices"
"sort"
)
// Idom returns the block that immediately dominates b:
// its parent in the dominator tree, if any.
// Neither the entry node (b.Index==0) nor recover node
// (b==b.Parent().Recover()) have a parent.
func (b *BasicBlock) Idom() *BasicBlock { return b.dom.idom }
// Dominees returns the list of blocks that b immediately dominates:
// its children in the dominator tree.
func (b *BasicBlock) Dominees() []*BasicBlock { return b.dom.children }
// Dominates reports whether b dominates c.
func (b *BasicBlock) Dominates(c *BasicBlock) bool {
return b.dom.pre <= c.dom.pre && c.dom.post <= b.dom.post
}
// DomPreorder returns a new slice containing the blocks of f
// in a preorder traversal of the dominator tree.
func (f *Function) DomPreorder() []*BasicBlock {
slice := slices.Clone(f.Blocks)
sort.Slice(slice, func(i, j int) bool {
return slice[i].dom.pre < slice[j].dom.pre
})
return slice
}
// DomPostorder returns a new slice containing the blocks of f
// in a postorder traversal of the dominator tree.
// (This is not the same as a postdominance order.)
func (f *Function) DomPostorder() []*BasicBlock {
slice := slices.Clone(f.Blocks)
sort.Slice(slice, func(i, j int) bool {
return slice[i].dom.post < slice[j].dom.post
})
return slice
}
// domInfo contains a BasicBlock's dominance information.
type domInfo struct {
idom *BasicBlock // immediate dominator (parent in domtree)
children []*BasicBlock // nodes immediately dominated by this one
pre, post int32 // pre- and post-order numbering within domtree
}
// ltState holds the working state for Lengauer-Tarjan algorithm
// (during which domInfo.pre is repurposed for CFG DFS preorder number).
type ltState struct {
// Each slice is indexed by b.Index.
sdom []*BasicBlock // b's semidominator
parent []*BasicBlock // b's parent in DFS traversal of CFG
ancestor []*BasicBlock // b's ancestor with least sdom
}
// dfs implements the depth-first search part of the LT algorithm.
func (lt *ltState) dfs(v *BasicBlock, i int32, preorder []*BasicBlock) int32 {
preorder[i] = v
v.dom.pre = i // For now: DFS preorder of spanning tree of CFG
i++
lt.sdom[v.Index] = v
lt.link(nil, v)
for _, w := range v.Succs {
if lt.sdom[w.Index] == nil {
lt.parent[w.Index] = v
i = lt.dfs(w, i, preorder)
}
}
return i
}
// eval implements the EVAL part of the LT algorithm.
func (lt *ltState) eval(v *BasicBlock) *BasicBlock {
// TODO(adonovan): opt: do path compression per simple LT.
u := v
for ; lt.ancestor[v.Index] != nil; v = lt.ancestor[v.Index] {
if lt.sdom[v.Index].dom.pre < lt.sdom[u.Index].dom.pre {
u = v
}
}
return u
}
// link implements the LINK part of the LT algorithm.
func (lt *ltState) link(v, w *BasicBlock) {
lt.ancestor[w.Index] = v
}
// buildDomTree computes the dominator tree of f using the LT algorithm.
// Precondition: all blocks are reachable (e.g. optimizeBlocks has been run).
func buildDomTree(f *Function) {
// The step numbers refer to the original LT paper; the
// reordering is due to Georgiadis.
// Clear any previous domInfo.
for _, b := range f.Blocks {
b.dom = domInfo{}
}
n := len(f.Blocks)
// Allocate space for 5 contiguous [n]*BasicBlock arrays:
// sdom, parent, ancestor, preorder, buckets.
space := make([]*BasicBlock, 5*n)
lt := ltState{
sdom: space[0:n],
parent: space[n : 2*n],
ancestor: space[2*n : 3*n],
}
// Step 1. Number vertices by depth-first preorder.
preorder := space[3*n : 4*n]
root := f.Blocks[0]
prenum := lt.dfs(root, 0, preorder)
recover := f.Recover
if recover != nil {
lt.dfs(recover, prenum, preorder)
}
buckets := space[4*n : 5*n]
copy(buckets, preorder)
// In reverse preorder...
for i := int32(n) - 1; i > 0; i-- {
w := preorder[i]
// Step 3. Implicitly define the immediate dominator of each node.
for v := buckets[i]; v != w; v = buckets[v.dom.pre] {
u := lt.eval(v)
if lt.sdom[u.Index].dom.pre < i {
v.dom.idom = u
} else {
v.dom.idom = w
}
}
// Step 2. Compute the semidominators of all nodes.
lt.sdom[w.Index] = lt.parent[w.Index]
for _, v := range w.Preds {
u := lt.eval(v)
if lt.sdom[u.Index].dom.pre < lt.sdom[w.Index].dom.pre {
lt.sdom[w.Index] = lt.sdom[u.Index]
}
}
lt.link(lt.parent[w.Index], w)
if lt.parent[w.Index] == lt.sdom[w.Index] {
w.dom.idom = lt.parent[w.Index]
} else {
buckets[i] = buckets[lt.sdom[w.Index].dom.pre]
buckets[lt.sdom[w.Index].dom.pre] = w
}
}
// The final 'Step 3' is now outside the loop.
for v := buckets[0]; v != root; v = buckets[v.dom.pre] {
v.dom.idom = root
}
// Step 4. Explicitly define the immediate dominator of each
// node, in preorder.
for _, w := range preorder[1:] {
if w == root || w == recover {
w.dom.idom = nil
} else {
if w.dom.idom != lt.sdom[w.Index] {
w.dom.idom = w.dom.idom.dom.idom
}
// Calculate Children relation as inverse of Idom.
w.dom.idom.dom.children = append(w.dom.idom.dom.children, w)
}
}
pre, post := numberDomTree(root, 0, 0)
if recover != nil {
numberDomTree(recover, pre, post)
}
// printDomTreeDot(os.Stderr, f) // debugging
// printDomTreeText(os.Stderr, root, 0) // debugging
if f.Prog.mode&SanityCheckFunctions != 0 {
sanityCheckDomTree(f)
}
}
// numberDomTree sets the pre- and post-order numbers of a depth-first
// traversal of the dominator tree rooted at v. These are used to
// answer dominance queries in constant time.
func numberDomTree(v *BasicBlock, pre, post int32) (int32, int32) {
v.dom.pre = pre
pre++
for _, child := range v.dom.children {
pre, post = numberDomTree(child, pre, post)
}
v.dom.post = post
post++
return pre, post
}
// Testing utilities ----------------------------------------
// sanityCheckDomTree checks the correctness of the dominator tree
// computed by the LT algorithm by comparing against the dominance
// relation computed by a naive Kildall-style forward dataflow
// analysis (Algorithm 10.16 from the "Dragon" book).
func sanityCheckDomTree(f *Function) {
n := len(f.Blocks)
// D[i] is the set of blocks that dominate f.Blocks[i],
// represented as a bit-set of block indices.
D := make([]big.Int, n)
one := big.NewInt(1)
// all is the set of all blocks; constant.
var all big.Int
all.Set(one).Lsh(&all, uint(n)).Sub(&all, one)
// Initialization.
for i, b := range f.Blocks {
if i == 0 || b == f.Recover {
// A root is dominated only by itself.
D[i].SetBit(&D[0], 0, 1)
} else {
// All other blocks are (initially) dominated
// by every block.
D[i].Set(&all)
}
}
// Iteration until fixed point.
for changed := true; changed; {
changed = false
for i, b := range f.Blocks {
if i == 0 || b == f.Recover {
continue
}
// Compute intersection across predecessors.
var x big.Int
x.Set(&all)
for _, pred := range b.Preds {
x.And(&x, &D[pred.Index])
}
x.SetBit(&x, i, 1) // a block always dominates itself.
if D[i].Cmp(&x) != 0 {
D[i].Set(&x)
changed = true
}
}
}
// Check the entire relation. O(n^2).
// The Recover block (if any) must be treated specially so we skip it.
ok := true
for i := range n {
for j := range n {
b, c := f.Blocks[i], f.Blocks[j]
if c == f.Recover {
continue
}
actual := b.Dominates(c)
expected := D[j].Bit(i) == 1
if actual != expected {
fmt.Fprintf(os.Stderr, "dominates(%s, %s)==%t, want %t\n", b, c, actual, expected)
ok = false
}
}
}
preorder := f.DomPreorder()
for _, b := range f.Blocks {
if got := preorder[b.dom.pre]; got != b {
fmt.Fprintf(os.Stderr, "preorder[%d]==%s, want %s\n", b.dom.pre, got, b)
ok = false
}
}
if !ok {
panic("sanityCheckDomTree failed for " + f.String())
}
}
// Printing functions ----------------------------------------
// printDomTreeText prints the dominator tree as text, using indentation.
func printDomTreeText(buf *bytes.Buffer, v *BasicBlock, indent int) {
fmt.Fprintf(buf, "%*s%s\n", 4*indent, "", v)
for _, child := range v.dom.children {
printDomTreeText(buf, child, indent+1)
}
}
// printDomTreeDot prints the dominator tree of f in AT&T GraphViz
// (.dot) format.
// (unused; retained for debugging)
func printDomTreeDot(buf *bytes.Buffer, f *Function) {
fmt.Fprintln(buf, "//", f)
fmt.Fprintln(buf, "digraph domtree {")
for i, b := range f.Blocks {
v := b.dom
fmt.Fprintf(buf, "\tn%d [label=\"%s (%d, %d)\",shape=\"rectangle\"];\n", v.pre, b, v.pre, v.post)
// TODO(adonovan): improve appearance of edges
// belonging to both dominator tree and CFG.
// Dominator tree edge.
if i != 0 {
fmt.Fprintf(buf, "\tn%d -> n%d [style=\"solid\",weight=100];\n", v.idom.dom.pre, v.pre)
}
// CFG edges.
for _, pred := range b.Preds {
fmt.Fprintf(buf, "\tn%d -> n%d [style=\"dotted\",weight=0];\n", pred.dom.pre, v.pre)
}
}
fmt.Fprintln(buf, "}")
}

629
vendor/golang.org/x/tools/go/ssa/emit.go generated vendored Normal file
View File

@@ -0,0 +1,629 @@
// Copyright 2013 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 ssa
// Helpers for emitting SSA instructions.
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/internal/typeparams"
)
// emitAlloc emits to f a new Alloc instruction allocating a variable
// of type typ.
//
// The caller must set Alloc.Heap=true (for a heap-allocated variable)
// or add the Alloc to f.Locals (for a frame-allocated variable).
//
// During building, a variable in f.Locals may have its Heap flag
// set when it is discovered that its address is taken.
// These Allocs are removed from f.Locals at the end.
//
// The builder should generally call one of the emit{New,Local,LocalVar} wrappers instead.
func emitAlloc(f *Function, typ types.Type, pos token.Pos, comment string) *Alloc {
v := &Alloc{Comment: comment}
v.setType(types.NewPointer(typ))
v.setPos(pos)
f.emit(v)
return v
}
// emitNew emits to f a new Alloc instruction heap-allocating a
// variable of type typ. pos is the optional source location.
func emitNew(f *Function, typ types.Type, pos token.Pos, comment string) *Alloc {
alloc := emitAlloc(f, typ, pos, comment)
alloc.Heap = true
return alloc
}
// emitLocal creates a local var for (t, pos, comment) and
// emits an Alloc instruction for it.
//
// (Use this function or emitNew for synthetic variables;
// for source-level variables in the same function, use emitLocalVar.)
func emitLocal(f *Function, t types.Type, pos token.Pos, comment string) *Alloc {
local := emitAlloc(f, t, pos, comment)
f.Locals = append(f.Locals, local)
return local
}
// emitLocalVar creates a local var for v and emits an Alloc instruction for it.
// Subsequent calls to f.lookup(v) return it.
// It applies the appropriate generic instantiation to the type.
func emitLocalVar(f *Function, v *types.Var) *Alloc {
alloc := emitLocal(f, f.typ(v.Type()), v.Pos(), v.Name())
f.vars[v] = alloc
return alloc
}
// emitLoad emits to f an instruction to load the address addr into a
// new temporary, and returns the value so defined.
func emitLoad(f *Function, addr Value) *UnOp {
v := &UnOp{Op: token.MUL, X: addr}
v.setType(typeparams.MustDeref(addr.Type()))
f.emit(v)
return v
}
// emitDebugRef emits to f a DebugRef pseudo-instruction associating
// expression e with value v.
func emitDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) {
if !f.debugInfo() {
return // debugging not enabled
}
if v == nil || e == nil {
panic("nil")
}
var obj types.Object
e = ast.Unparen(e)
if id, ok := e.(*ast.Ident); ok {
if isBlankIdent(id) {
return
}
obj = f.objectOf(id)
switch obj.(type) {
case *types.Nil, *types.Const, *types.Builtin:
return
}
}
f.emit(&DebugRef{
X: v,
Expr: e,
IsAddr: isAddr,
object: obj,
})
}
// emitArith emits to f code to compute the binary operation op(x, y)
// where op is an eager shift, logical or arithmetic operation.
// (Use emitCompare() for comparisons and Builder.logicalBinop() for
// non-eager operations.)
func emitArith(f *Function, op token.Token, x, y Value, t types.Type, pos token.Pos) Value {
switch op {
case token.SHL, token.SHR:
x = emitConv(f, x, t)
// y may be signed or an 'untyped' constant.
// There is a runtime panic if y is signed and <0. Instead of inserting a check for y<0
// and converting to an unsigned value (like the compiler) leave y as is.
if isUntyped(y.Type().Underlying()) {
// Untyped conversion:
// Spec https://go.dev/ref/spec#Operators:
// The right operand in a shift expression must have integer type or be an untyped constant
// representable by a value of type uint.
y = emitConv(f, y, types.Typ[types.Uint])
}
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
x = emitConv(f, x, t)
y = emitConv(f, y, t)
default:
panic("illegal op in emitArith: " + op.String())
}
v := &BinOp{
Op: op,
X: x,
Y: y,
}
v.setPos(pos)
v.setType(t)
return f.emit(v)
}
// emitCompare emits to f code compute the boolean result of
// comparison 'x op y'.
func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value {
xt := x.Type().Underlying()
yt := y.Type().Underlying()
// Special case to optimise a tagless SwitchStmt so that
// these are equivalent
// switch { case e: ...}
// switch true { case e: ... }
// if e==true { ... }
// even in the case when e's type is an interface.
// TODO(adonovan): opt: generalise to x==true, false!=y, etc.
if x == vTrue && op == token.EQL {
if yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 {
return y
}
}
if types.Identical(xt, yt) {
// no conversion necessary
} else if isNonTypeParamInterface(x.Type()) {
y = emitConv(f, y, x.Type())
} else if isNonTypeParamInterface(y.Type()) {
x = emitConv(f, x, y.Type())
} else if _, ok := x.(*Const); ok {
x = emitConv(f, x, y.Type())
} else if _, ok := y.(*Const); ok {
y = emitConv(f, y, x.Type())
} else {
// other cases, e.g. channels. No-op.
}
v := &BinOp{
Op: op,
X: x,
Y: y,
}
v.setPos(pos)
v.setType(tBool)
return f.emit(v)
}
// isValuePreserving returns true if a conversion from ut_src to
// ut_dst is value-preserving, i.e. just a change of type.
// Precondition: neither argument is a named or alias type.
func isValuePreserving(ut_src, ut_dst types.Type) bool {
// Identical underlying types?
if types.IdenticalIgnoreTags(ut_dst, ut_src) {
return true
}
switch ut_dst.(type) {
case *types.Chan:
// Conversion between channel types?
_, ok := ut_src.(*types.Chan)
return ok
case *types.Pointer:
// Conversion between pointers with identical base types?
_, ok := ut_src.(*types.Pointer)
return ok
}
return false
}
// emitConv emits to f code to convert Value val to exactly type typ,
// and returns the converted value. Implicit conversions are required
// by language assignability rules in assignments, parameter passing,
// etc.
func emitConv(f *Function, val Value, typ types.Type) Value {
t_src := val.Type()
// Identical types? Conversion is a no-op.
if types.Identical(t_src, typ) {
return val
}
ut_dst := typ.Underlying()
ut_src := t_src.Underlying()
// Conversion to, or construction of a value of, an interface type?
if isNonTypeParamInterface(typ) {
// Interface name change?
if isValuePreserving(ut_src, ut_dst) {
c := &ChangeType{X: val}
c.setType(typ)
return f.emit(c)
}
// Assignment from one interface type to another?
if isNonTypeParamInterface(t_src) {
c := &ChangeInterface{X: val}
c.setType(typ)
return f.emit(c)
}
// Untyped nil constant? Return interface-typed nil constant.
if ut_src == tUntypedNil {
return zeroConst(typ)
}
// Convert (non-nil) "untyped" literals to their default type.
if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 {
val = emitConv(f, val, types.Default(ut_src))
}
// Record the types of operands to MakeInterface, if
// non-parameterized, as they are the set of runtime types.
t := val.Type()
if f.typeparams.Len() == 0 || !f.Prog.isParameterized(t) {
addMakeInterfaceType(f.Prog, t)
}
mi := &MakeInterface{X: val}
mi.setType(typ)
return f.emit(mi)
}
// conversionCase describes an instruction pattern that maybe emitted to
// model d <- s for d in dst_terms and s in src_terms.
// Multiple conversions can match the same pattern.
type conversionCase uint8
const (
changeType conversionCase = 1 << iota
sliceToArray
sliceToArrayPtr
sliceTo0Array
sliceTo0ArrayPtr
convert
)
// classify the conversion case of a source type us to a destination type ud.
// us and ud are underlying types (not *Named or *Alias)
classify := func(us, ud types.Type) conversionCase {
// Just a change of type, but not value or representation?
if isValuePreserving(us, ud) {
return changeType
}
// Conversion from slice to array or slice to array pointer?
if slice, ok := us.(*types.Slice); ok {
var arr *types.Array
var ptr bool
// Conversion from slice to array pointer?
switch d := ud.(type) {
case *types.Array:
arr = d
case *types.Pointer:
arr, _ = d.Elem().Underlying().(*types.Array)
ptr = true
}
if arr != nil && types.Identical(slice.Elem(), arr.Elem()) {
if arr.Len() == 0 {
if ptr {
return sliceTo0ArrayPtr
} else {
return sliceTo0Array
}
}
if ptr {
return sliceToArrayPtr
} else {
return sliceToArray
}
}
}
// The only remaining case in well-typed code is a representation-
// changing conversion of basic types (possibly with []byte/[]rune).
if !isBasic(us) && !isBasic(ud) {
panic(fmt.Sprintf("in %s: cannot convert term %s (%s [within %s]) to type %s [within %s]", f, val, val.Type(), us, typ, ud))
}
return convert
}
var classifications conversionCase
underIs(ut_src, func(us types.Type) bool {
return underIs(ut_dst, func(ud types.Type) bool {
if us != nil && ud != nil {
classifications |= classify(us, ud)
}
return classifications != 0
})
})
if classifications == 0 {
panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), typ))
}
// Conversion of a compile-time constant value?
if c, ok := val.(*Const); ok {
// Conversion to a basic type?
if isBasic(ut_dst) {
// Conversion of a compile-time constant to
// another constant type results in a new
// constant of the destination type and
// (initially) the same abstract value.
// We don't truncate the value yet.
return NewConst(c.Value, typ)
}
// Can we always convert from zero value without panicking?
const mayPanic = sliceToArray | sliceToArrayPtr
if c.Value == nil && classifications&mayPanic == 0 {
return NewConst(nil, typ)
}
// We're converting from constant to non-constant type,
// e.g. string -> []byte/[]rune.
}
switch classifications {
case changeType: // representation-preserving change
c := &ChangeType{X: val}
c.setType(typ)
return f.emit(c)
case sliceToArrayPtr, sliceTo0ArrayPtr: // slice to array pointer
c := &SliceToArrayPointer{X: val}
c.setType(typ)
return f.emit(c)
case sliceToArray: // slice to arrays (not zero-length)
ptype := types.NewPointer(typ)
p := &SliceToArrayPointer{X: val}
p.setType(ptype)
x := f.emit(p)
unOp := &UnOp{Op: token.MUL, X: x}
unOp.setType(typ)
return f.emit(unOp)
case sliceTo0Array: // slice to zero-length arrays (constant)
return zeroConst(typ)
case convert: // representation-changing conversion
c := &Convert{X: val}
c.setType(typ)
return f.emit(c)
default: // The conversion represents a cross product.
c := &MultiConvert{X: val, from: t_src, to: typ}
c.setType(typ)
return f.emit(c)
}
}
// emitTypeCoercion emits to f code to coerce the type of a
// Value v to exactly type typ, and returns the coerced value.
//
// Requires that coercing v.Typ() to typ is a value preserving change.
//
// Currently used only when v.Type() is a type instance of typ or vice versa.
// A type v is a type instance of a type t if there exists a
// type parameter substitution σ s.t. σ(v) == t. Example:
//
// σ(func(T) T) == func(int) int for σ == [T ↦ int]
//
// This happens in instantiation wrappers for conversion
// from an instantiation to a parameterized type (and vice versa)
// with σ substituting f.typeparams by f.typeargs.
func emitTypeCoercion(f *Function, v Value, typ types.Type) Value {
if types.Identical(v.Type(), typ) {
return v // no coercion needed
}
// TODO(taking): for instances should we record which side is the instance?
c := &ChangeType{
X: v,
}
c.setType(typ)
f.emit(c)
return c
}
// emitStore emits to f an instruction to store value val at location
// addr, applying implicit conversions as required by assignability rules.
func emitStore(f *Function, addr, val Value, pos token.Pos) *Store {
typ := typeparams.MustDeref(addr.Type())
s := &Store{
Addr: addr,
Val: emitConv(f, val, typ),
pos: pos,
}
f.emit(s)
return s
}
// emitJump emits to f a jump to target, and updates the control-flow graph.
// Postcondition: f.currentBlock is nil.
func emitJump(f *Function, target *BasicBlock) {
b := f.currentBlock
b.emit(new(Jump))
addEdge(b, target)
f.currentBlock = nil
}
// emitIf emits to f a conditional jump to tblock or fblock based on
// cond, and updates the control-flow graph.
// Postcondition: f.currentBlock is nil.
func emitIf(f *Function, cond Value, tblock, fblock *BasicBlock) {
b := f.currentBlock
b.emit(&If{Cond: cond})
addEdge(b, tblock)
addEdge(b, fblock)
f.currentBlock = nil
}
// emitExtract emits to f an instruction to extract the index'th
// component of tuple. It returns the extracted value.
func emitExtract(f *Function, tuple Value, index int) Value {
e := &Extract{Tuple: tuple, Index: index}
e.setType(tuple.Type().(*types.Tuple).At(index).Type())
return f.emit(e)
}
// emitTypeAssert emits to f a type assertion value := x.(t) and
// returns the value. x.Type() must be an interface.
func emitTypeAssert(f *Function, x Value, t types.Type, pos token.Pos) Value {
a := &TypeAssert{X: x, AssertedType: t}
a.setPos(pos)
a.setType(t)
return f.emit(a)
}
// emitTypeTest emits to f a type test value,ok := x.(t) and returns
// a (value, ok) tuple. x.Type() must be an interface.
func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value {
a := &TypeAssert{
X: x,
AssertedType: t,
CommaOk: true,
}
a.setPos(pos)
a.setType(types.NewTuple(
newVar("value", t),
varOk,
))
return f.emit(a)
}
// emitTailCall emits to f a function call in tail position. The
// caller is responsible for all fields of 'call' except its type.
// Intended for wrapper methods.
// Precondition: f does/will not use deferred procedure calls.
// Postcondition: f.currentBlock is nil.
func emitTailCall(f *Function, call *Call) {
tresults := f.Signature.Results()
nr := tresults.Len()
if nr == 1 {
call.typ = tresults.At(0).Type()
} else {
call.typ = tresults
}
tuple := emitCall(f, call)
var ret Return
switch nr {
case 0:
// no-op
case 1:
ret.Results = []Value{tuple}
default:
for i := range nr {
v := emitExtract(f, tuple, i)
// TODO(adonovan): in principle, this is required:
// v = emitConv(f, o.Type, f.Signature.Results[i].Type)
// but in practice emitTailCall is only used when
// the types exactly match.
ret.Results = append(ret.Results, v)
}
}
f.emit(&ret)
f.currentBlock = nil
}
// emitCall emits a call instruction. If the callee is "no return",
// it also emits a panic to eliminate infeasible CFG edges.
func emitCall(fn *Function, call *Call) Value {
res := fn.emit(call)
callee := call.Call.StaticCallee()
if callee != nil &&
callee.object != nil &&
fn.Prog.noReturn != nil &&
fn.Prog.noReturn(callee.object) {
// Call cannot return. Insert a panic after it.
fn.emit(&Panic{
X: emitConv(fn, vNoReturn, tEface),
pos: call.Pos(),
})
fn.currentBlock = fn.newBasicBlock("unreachable.noreturn")
}
return res
}
// emitImplicitSelections emits to f code to apply the sequence of
// implicit field selections specified by indices to base value v, and
// returns the selected value.
//
// If v is the address of a struct, the result will be the address of
// a field; if it is the value of a struct, the result will be the
// value of a field.
func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos) Value {
for _, index := range indices {
if isPointerCore(v.Type()) {
fld := fieldOf(typeparams.MustDeref(v.Type()), index)
instr := &FieldAddr{
X: v,
Field: index,
}
instr.setPos(pos)
instr.setType(types.NewPointer(fld.Type()))
v = f.emit(instr)
// Load the field's value iff indirectly embedded.
if isPointerCore(fld.Type()) {
v = emitLoad(f, v)
}
} else {
fld := fieldOf(v.Type(), index)
instr := &Field{
X: v,
Field: index,
}
instr.setPos(pos)
instr.setType(fld.Type())
v = f.emit(instr)
}
}
return v
}
// emitFieldSelection emits to f code to select the index'th field of v.
//
// If wantAddr, the input must be a pointer-to-struct and the result
// will be the field's address; otherwise the result will be the
// field's value.
// Ident id is used for position and debug info.
func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {
if isPointerCore(v.Type()) {
fld := fieldOf(typeparams.MustDeref(v.Type()), index)
instr := &FieldAddr{
X: v,
Field: index,
}
instr.setPos(id.Pos())
instr.setType(types.NewPointer(fld.Type()))
v = f.emit(instr)
// Load the field's value iff we don't want its address.
if !wantAddr {
v = emitLoad(f, v)
}
} else {
fld := fieldOf(v.Type(), index)
instr := &Field{
X: v,
Field: index,
}
instr.setPos(id.Pos())
instr.setType(fld.Type())
v = f.emit(instr)
}
emitDebugRef(f, id, v, wantAddr)
return v
}
// createRecoverBlock emits to f a block of code to return after a
// recovered panic, and sets f.Recover to it.
//
// If f's result parameters are named, the code loads and returns
// their current values, otherwise it returns the zero values of their
// type.
//
// Idempotent.
func createRecoverBlock(f *Function) {
if f.Recover != nil {
return // already created
}
saved := f.currentBlock
f.Recover = f.newBasicBlock("recover")
f.currentBlock = f.Recover
var results []Value
// Reload NRPs to form value tuple.
for _, nr := range f.results {
results = append(results, emitLoad(f, nr))
}
f.emit(&Return{Results: results})
f.currentBlock = saved
}

836
vendor/golang.org/x/tools/go/ssa/func.go generated vendored Normal file
View File

@@ -0,0 +1,836 @@
// Copyright 2013 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 ssa
// This file implements the Function type.
import (
"bytes"
"fmt"
"go/ast"
"go/token"
"go/types"
"io"
"iter"
"os"
"strings"
"golang.org/x/tools/internal/typeparams"
)
// Like ObjectOf, but panics instead of returning nil.
// Only valid during f's create and build phases.
func (f *Function) objectOf(id *ast.Ident) types.Object {
if o := f.info.ObjectOf(id); o != nil {
return o
}
panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s",
id.Name, f.Prog.Fset.Position(id.Pos())))
}
// Like TypeOf, but panics instead of returning nil.
// Only valid during f's create and build phases.
func (f *Function) typeOf(e ast.Expr) types.Type {
if T := f.info.TypeOf(e); T != nil {
return f.typ(T)
}
panic(fmt.Sprintf("no type for %T @ %s", e, f.Prog.Fset.Position(e.Pos())))
}
// typ is the locally instantiated type of T.
// If f is not an instantiation, then f.typ(T)==T.
func (f *Function) typ(T types.Type) types.Type {
return f.subst.typ(T)
}
// If id is an Instance, returns info.Instances[id].Type.
// Otherwise returns f.typeOf(id).
func (f *Function) instanceType(id *ast.Ident) types.Type {
if t, ok := f.info.Instances[id]; ok {
return t.Type
}
return f.typeOf(id)
}
// selection returns a *selection corresponding to f.info.Selections[selector]
// with potential updates for type substitution.
func (f *Function) selection(selector *ast.SelectorExpr) *selection {
sel := f.info.Selections[selector]
if sel == nil {
return nil
}
switch sel.Kind() {
case types.MethodExpr, types.MethodVal:
if recv := f.typ(sel.Recv()); recv != sel.Recv() {
// recv changed during type substitution.
pkg := f.declaredPackage().Pkg
obj, index, indirect := types.LookupFieldOrMethod(recv, true, pkg, sel.Obj().Name())
// sig replaces sel.Type(). See (types.Selection).Typ() for details.
sig := obj.Type().(*types.Signature)
sig = changeRecv(sig, newVar(sig.Recv().Name(), recv))
if sel.Kind() == types.MethodExpr {
sig = recvAsFirstArg(sig)
}
return &selection{
kind: sel.Kind(),
recv: recv,
typ: sig,
obj: obj,
index: index,
indirect: indirect,
}
}
}
return toSelection(sel)
}
// Destinations associated with unlabelled for/switch/select stmts.
// We push/pop one of these as we enter/leave each construct and for
// each BranchStmt we scan for the innermost target of the right type.
type targets struct {
tail *targets // rest of stack
_break *BasicBlock
_continue *BasicBlock
_fallthrough *BasicBlock
}
// Destinations associated with a labelled block.
// We populate these as labels are encountered in forward gotos or
// labelled statements.
// Forward gotos are resolved once it is known which statement they
// are associated with inside the Function.
type lblock struct {
label *types.Label // Label targeted by the blocks.
resolved bool // _goto block encountered (back jump or resolved fwd jump)
_goto *BasicBlock
_break *BasicBlock
_continue *BasicBlock
}
// label returns the symbol denoted by a label identifier.
//
// label should be a non-blank identifier (label.Name != "_").
func (f *Function) label(label *ast.Ident) *types.Label {
return f.objectOf(label).(*types.Label)
}
// lblockOf returns the branch target associated with the
// specified label, creating it if needed.
func (f *Function) lblockOf(label *types.Label) *lblock {
lb := f.lblocks[label]
if lb == nil {
lb = &lblock{
label: label,
_goto: f.newBasicBlock(label.Name()),
}
if f.lblocks == nil {
f.lblocks = make(map[*types.Label]*lblock)
}
f.lblocks[label] = lb
}
return lb
}
// labelledBlock searches f for the block of the specified label.
//
// If f is a yield function, it additionally searches ancestor Functions
// corresponding to enclosing range-over-func statements within the
// same source function, so the returned block may belong to a different Function.
func labelledBlock(f *Function, label *types.Label, tok token.Token) *BasicBlock {
if lb := f.lblocks[label]; lb != nil {
var block *BasicBlock
switch tok {
case token.BREAK:
block = lb._break
case token.CONTINUE:
block = lb._continue
case token.GOTO:
block = lb._goto
}
if block != nil {
return block
}
}
// Search ancestors if this is a yield function.
if f.jump != nil {
return labelledBlock(f.parent, label, tok)
}
return nil
}
// targetedBlock looks for the nearest block in f.targets
// (and f's ancestors) that matches tok's type, and returns
// the block and function it was found in.
func targetedBlock(f *Function, tok token.Token) *BasicBlock {
if f == nil {
return nil
}
for t := f.targets; t != nil; t = t.tail {
var block *BasicBlock
switch tok {
case token.BREAK:
block = t._break
case token.CONTINUE:
block = t._continue
case token.FALLTHROUGH:
block = t._fallthrough
}
if block != nil {
return block
}
}
// Search f's ancestors (in case f is a yield function).
return targetedBlock(f.parent, tok)
}
// instrs returns an iterator that returns each reachable instruction of the SSA function.
func (f *Function) instrs() iter.Seq[Instruction] {
return func(yield func(i Instruction) bool) {
for _, block := range f.Blocks {
for _, instr := range block.Instrs {
if !yield(instr) {
return
}
}
}
}
}
// addResultVar adds a result for a variable v to f.results and v to f.returnVars.
func (f *Function) addResultVar(v *types.Var) {
result := emitLocalVar(f, v)
f.results = append(f.results, result)
f.returnVars = append(f.returnVars, v)
}
// addParamVar adds a parameter to f.Params.
func (f *Function) addParamVar(v *types.Var) *Parameter {
name := v.Name()
if name == "" {
name = fmt.Sprintf("arg%d", len(f.Params))
}
param := &Parameter{
name: name,
object: v,
typ: f.typ(v.Type()),
parent: f,
}
f.Params = append(f.Params, param)
return param
}
// addSpilledParam declares a parameter that is pre-spilled to the
// stack; the function body will load/store the spilled location.
// Subsequent lifting will eliminate spills where possible.
func (f *Function) addSpilledParam(obj *types.Var) {
param := f.addParamVar(obj)
spill := emitLocalVar(f, obj)
f.emit(&Store{Addr: spill, Val: param})
}
// startBody initializes the function prior to generating SSA code for its body.
// Precondition: f.Type() already set.
func (f *Function) startBody() {
f.currentBlock = f.newBasicBlock("entry")
f.vars = make(map[*types.Var]Value) // needed for some synthetics, e.g. init
}
// createSyntacticParams populates f.Params and generates code (spills
// and named result locals) for all the parameters declared in the
// syntax. In addition it populates the f.objects mapping.
//
// Preconditions:
// f.startBody() was called. f.info != nil.
// Postcondition:
// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) {
// Receiver (at most one inner iteration).
if recv != nil {
for _, field := range recv.List {
for _, n := range field.Names {
f.addSpilledParam(identVar(f, n))
}
// Anonymous receiver? No need to spill.
if field.Names == nil {
f.addParamVar(f.Signature.Recv())
}
}
}
// Parameters.
if functype.Params != nil {
n := len(f.Params) // 1 if has recv, 0 otherwise
for _, field := range functype.Params.List {
for _, n := range field.Names {
f.addSpilledParam(identVar(f, n))
}
// Anonymous parameter? No need to spill.
if field.Names == nil {
f.addParamVar(f.Signature.Params().At(len(f.Params) - n))
}
}
}
// Results.
if functype.Results != nil {
for _, field := range functype.Results.List {
// Implicit "var" decl of locals for named results.
for _, n := range field.Names {
v := identVar(f, n)
f.addResultVar(v)
}
// Implicit "var" decl of local for an unnamed result.
if field.Names == nil {
v := f.Signature.Results().At(len(f.results))
f.addResultVar(v)
}
}
}
}
// createDeferStack initializes fn.deferstack to local variable
// initialized to a ssa:deferstack() call.
func (fn *Function) createDeferStack() {
// Each syntactic function makes a call to ssa:deferstack,
// which is spilled to a local. Unused ones are later removed.
fn.deferstack = newVar("defer$stack", tDeferStack)
call := &Call{Call: CallCommon{Value: vDeferStack}}
call.setType(tDeferStack)
deferstack := fn.emit(call)
spill := emitLocalVar(fn, fn.deferstack)
emitStore(fn, spill, deferstack, token.NoPos)
}
type setNumable interface {
setNum(int)
}
// numberRegisters assigns numbers to all SSA registers
// (value-defining Instructions) in f, to aid debugging.
// (Non-Instruction Values are named at construction.)
func numberRegisters(f *Function) {
v := 0
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
switch instr.(type) {
case Value:
instr.(setNumable).setNum(v)
v++
}
}
}
}
// buildReferrers populates the def/use information in all non-nil
// Value.Referrers slice.
// Precondition: all such slices are initially empty.
func buildReferrers(f *Function) {
var rands []*Value
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
rands = instr.Operands(rands[:0]) // recycle storage
for _, rand := range rands {
if r := *rand; r != nil {
if ref := r.Referrers(); ref != nil {
*ref = append(*ref, instr)
}
}
}
}
}
}
// finishBody() finalizes the contents of the function after SSA code generation of its body.
//
// The function is not done being built until done() is called.
func (f *Function) finishBody() {
f.currentBlock = nil
f.lblocks = nil
f.returnVars = nil
f.jump = nil
f.source = nil
f.exits = nil
// Remove from f.Locals any Allocs that escape to the heap.
j := 0
for _, l := range f.Locals {
if !l.Heap {
f.Locals[j] = l
j++
}
}
// Nil out f.Locals[j:] to aid GC.
for i := j; i < len(f.Locals); i++ {
f.Locals[i] = nil
}
f.Locals = f.Locals[:j]
optimizeBlocks(f)
buildReferrers(f)
buildDomTree(f)
if f.Prog.mode&NaiveForm == 0 {
// For debugging pre-state of lifting pass:
// numberRegisters(f)
// f.WriteTo(os.Stderr)
lift(f)
}
// clear remaining builder state
f.results = nil // (used by lifting)
f.deferstack = nil // (used by lifting)
f.vars = nil // (used by lifting)
// clear out other function state (keep consistent with buildParamsOnly)
f.subst = nil
numberRegisters(f) // uses f.namedRegisters
}
// done marks the building of f's SSA body complete,
// along with any nested functions, and optionally prints them.
func (f *Function) done() {
assert(f.parent == nil, "done called on an anonymous function")
var visit func(*Function)
visit = func(f *Function) {
for _, anon := range f.AnonFuncs {
visit(anon) // anon is done building before f.
}
f.uniq = 0 // done with uniq
f.build = nil // function is built
if f.Prog.mode&PrintFunctions != 0 {
printMu.Lock()
f.WriteTo(os.Stdout)
printMu.Unlock()
}
if f.Prog.mode&SanityCheckFunctions != 0 {
mustSanityCheck(f, nil)
}
}
visit(f)
}
// removeNilBlocks eliminates nils from f.Blocks and updates each
// BasicBlock.Index. Use this after any pass that may delete blocks.
func (f *Function) removeNilBlocks() {
j := 0
for _, b := range f.Blocks {
if b != nil {
b.Index = j
f.Blocks[j] = b
j++
}
}
// Nil out f.Blocks[j:] to aid GC.
for i := j; i < len(f.Blocks); i++ {
f.Blocks[i] = nil
}
f.Blocks = f.Blocks[:j]
}
// SetDebugMode sets the debug mode for package pkg. If true, all its
// functions will include full debug info. This greatly increases the
// size of the instruction stream, and causes Functions to depend upon
// the ASTs, potentially keeping them live in memory for longer.
func (pkg *Package) SetDebugMode(debug bool) {
pkg.debug = debug
}
// debugInfo reports whether debug info is wanted for this function.
func (f *Function) debugInfo() bool {
// debug info for instantiations follows the debug info of their origin.
p := f.declaredPackage()
return p != nil && p.debug
}
// lookup returns the address of the named variable identified by obj
// that is local to function f or one of its enclosing functions.
// If escaping, the reference comes from a potentially escaping pointer
// expression and the referent must be heap-allocated.
// We assume the referent is a *Alloc or *Phi.
// (The only Phis at this stage are those created directly by go1.22 "for" loops.)
func (f *Function) lookup(obj *types.Var, escaping bool) Value {
if v, ok := f.vars[obj]; ok {
if escaping {
switch v := v.(type) {
case *Alloc:
v.Heap = true
case *Phi:
for _, edge := range v.Edges {
if alloc, ok := edge.(*Alloc); ok {
alloc.Heap = true
}
}
}
}
return v // function-local var (address)
}
// Definition must be in an enclosing function;
// plumb it through intervening closures.
if f.parent == nil {
panic("no ssa.Value for " + obj.String())
}
outer := f.parent.lookup(obj, true) // escaping
v := &FreeVar{
name: obj.Name(),
typ: outer.Type(),
pos: outer.Pos(),
outer: outer,
parent: f,
}
f.vars[obj] = v
f.FreeVars = append(f.FreeVars, v)
return v
}
// emit emits the specified instruction to function f.
func (f *Function) emit(instr Instruction) Value {
return f.currentBlock.emit(instr)
}
// RelString returns the full name of this function, qualified by
// package name, receiver type, etc.
//
// The specific formatting rules are not guaranteed and may change.
//
// Examples:
//
// "math.IsNaN" // a package-level function
// "(*bytes.Buffer).Bytes" // a declared method or a wrapper
// "(*bytes.Buffer).Bytes$thunk" // thunk (func wrapping method; receiver is param 0)
// "(*bytes.Buffer).Bytes$bound" // bound (func wrapping method; receiver supplied by closure)
// "main.main$1" // an anonymous function in main
// "main.init#1" // a declared init function
// "main.init" // the synthesized package initializer
//
// When these functions are referred to from within the same package
// (i.e. from == f.Pkg.Object), they are rendered without the package path.
// For example: "IsNaN", "(*Buffer).Bytes", etc.
//
// All non-synthetic functions have distinct package-qualified names.
// (But two methods may have the same name "(T).f" if one is a synthetic
// wrapper promoting a non-exported method "f" from another package; in
// that case, the strings are equal but the identifiers "f" are distinct.)
func (f *Function) RelString(from *types.Package) string {
// Anonymous?
if f.parent != nil {
// An anonymous function's Name() looks like "parentName$1",
// but its String() should include the type/package/etc.
parent := f.parent.RelString(from)
for i, anon := range f.parent.AnonFuncs {
if anon == f {
return fmt.Sprintf("%s$%d", parent, 1+i)
}
}
return f.name // should never happen
}
// Method (declared or wrapper)?
if recv := f.Signature.Recv(); recv != nil {
return f.relMethod(from, recv.Type())
}
// Thunk?
if f.method != nil {
return f.relMethod(from, f.method.recv)
}
// Bound?
if len(f.FreeVars) == 1 && strings.HasSuffix(f.name, "$bound") {
return f.relMethod(from, f.FreeVars[0].Type())
}
// Package-level function?
// Prefix with package name for cross-package references only.
if p := f.relPkg(); p != nil && p != from {
return fmt.Sprintf("%s.%s", p.Path(), f.name)
}
// Unknown.
return f.name
}
func (f *Function) relMethod(from *types.Package, recv types.Type) string {
return fmt.Sprintf("(%s).%s", relType(recv, from), f.name)
}
// writeSignature writes to buf the signature sig in declaration syntax.
func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature) {
buf.WriteString("func ")
if recv := sig.Recv(); recv != nil {
buf.WriteString("(")
if name := recv.Name(); name != "" {
buf.WriteString(name)
buf.WriteString(" ")
}
types.WriteType(buf, recv.Type(), types.RelativeTo(from))
buf.WriteString(") ")
}
buf.WriteString(name)
types.WriteSignature(buf, sig, types.RelativeTo(from))
}
// declaredPackage returns the package fn is declared in or nil if the
// function is not declared in a package.
func (fn *Function) declaredPackage() *Package {
switch {
case fn.Pkg != nil:
return fn.Pkg // non-generic function (does that follow??)
case fn.topLevelOrigin != nil:
return fn.topLevelOrigin.Pkg // instance of a named generic function
case fn.parent != nil:
return fn.parent.declaredPackage() // instance of an anonymous [generic] function
default:
return nil // function is not declared in a package, e.g. a wrapper.
}
}
// relPkg returns types.Package fn is printed in relationship to.
func (fn *Function) relPkg() *types.Package {
if p := fn.declaredPackage(); p != nil {
return p.Pkg
}
return nil
}
var _ io.WriterTo = (*Function)(nil) // *Function implements io.Writer
func (f *Function) WriteTo(w io.Writer) (int64, error) {
var buf bytes.Buffer
WriteFunction(&buf, f)
n, err := w.Write(buf.Bytes())
return int64(n), err
}
// WriteFunction writes to buf a human-readable "disassembly" of f.
func WriteFunction(buf *bytes.Buffer, f *Function) {
fmt.Fprintf(buf, "# Name: %s\n", f.String())
if f.Pkg != nil {
fmt.Fprintf(buf, "# Package: %s\n", f.Pkg.Pkg.Path())
}
if syn := f.Synthetic; syn != "" {
fmt.Fprintln(buf, "# Synthetic:", syn)
}
if pos := f.Pos(); pos.IsValid() {
fmt.Fprintf(buf, "# Location: %s\n", f.Prog.Fset.Position(pos))
}
if f.parent != nil {
fmt.Fprintf(buf, "# Parent: %s\n", f.parent.Name())
}
if f.Recover != nil {
fmt.Fprintf(buf, "# Recover: %s\n", f.Recover)
}
from := f.relPkg()
if f.FreeVars != nil {
buf.WriteString("# Free variables:\n")
for i, fv := range f.FreeVars {
fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), from))
}
}
if len(f.Locals) > 0 {
buf.WriteString("# Locals:\n")
for i, l := range f.Locals {
fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(typeparams.MustDeref(l.Type()), from))
}
}
writeSignature(buf, from, f.Name(), f.Signature)
buf.WriteString(":\n")
if f.Blocks == nil {
buf.WriteString("\t(external)\n")
}
// NB. column calculations are confused by non-ASCII
// characters and assume 8-space tabs.
const punchcard = 80 // for old time's sake.
const tabwidth = 8
for _, b := range f.Blocks {
if b == nil {
// Corrupt CFG.
fmt.Fprintf(buf, ".nil:\n")
continue
}
n, _ := fmt.Fprintf(buf, "%d:", b.Index)
// (|predecessors|, |successors|, immediate dominator)
bmsg := fmt.Sprintf("%s P:%d S:%d", b.Comment, len(b.Preds), len(b.Succs))
if b.Idom() != nil {
bmsg = fmt.Sprintf("%s idom:%d", bmsg, b.Idom().Index)
}
fmt.Fprintf(buf, "%*s%s\n", punchcard-1-n-len(bmsg), "", bmsg)
if false { // CFG debugging
fmt.Fprintf(buf, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs)
}
for _, instr := range b.Instrs {
buf.WriteString("\t")
switch v := instr.(type) {
case Value:
l := punchcard - tabwidth
// Left-align the instruction.
if name := v.Name(); name != "" {
n, _ := fmt.Fprintf(buf, "%s = ", name)
l -= n
}
n, _ := buf.WriteString(instr.String())
l -= n
// Right-align the type if there's space.
if t := v.Type(); t != nil {
buf.WriteByte(' ')
ts := relType(t, from)
l -= len(ts) + len(" ") // (spaces before and after type)
if l > 0 {
fmt.Fprintf(buf, "%*s", l, "")
}
buf.WriteString(ts)
}
case nil:
// Be robust against bad transforms.
buf.WriteString("<deleted>")
default:
buf.WriteString(instr.String())
}
// -mode=S: show line numbers
if f.Prog.mode&LogSource != 0 {
if pos := instr.Pos(); pos.IsValid() {
fmt.Fprintf(buf, " L%d", f.Prog.Fset.Position(pos).Line)
}
}
buf.WriteString("\n")
}
}
fmt.Fprintf(buf, "\n")
}
// newBasicBlock adds to f a new basic block and returns it. It does
// not automatically become the current block for subsequent calls to emit.
// comment is an optional string for more readable debugging output.
func (f *Function) newBasicBlock(comment string) *BasicBlock {
b := &BasicBlock{
Index: len(f.Blocks),
Comment: comment,
parent: f,
}
b.Succs = b.succs2[:0]
f.Blocks = append(f.Blocks, b)
return b
}
// NewFunction returns a new synthetic Function instance belonging to
// prog, with its name and signature fields set as specified.
//
// The caller is responsible for initializing the remaining fields of
// the function object, e.g. Pkg, Params, Blocks.
//
// It is practically impossible for clients to construct well-formed
// SSA functions/packages/programs directly, so we assume this is the
// job of the Builder alone. NewFunction exists to provide clients a
// little flexibility. For example, analysis tools may wish to
// construct fake Functions for the root of the callgraph, a fake
// "reflect" package, etc.
//
// TODO(adonovan): think harder about the API here.
func (prog *Program) NewFunction(name string, sig *types.Signature, provenance string) *Function {
return &Function{Prog: prog, name: name, Signature: sig, Synthetic: provenance}
}
// Syntax returns the function's syntax (*ast.Func{Decl,Lit})
// if it was produced from syntax or an *ast.RangeStmt if
// it is a range-over-func yield function.
func (f *Function) Syntax() ast.Node { return f.syntax }
// identVar returns the variable defined by id.
func identVar(fn *Function, id *ast.Ident) *types.Var {
return fn.info.Defs[id].(*types.Var)
}
// unique returns a unique positive int within the source tree of f.
// The source tree of f includes all of f's ancestors by parent and all
// of the AnonFuncs contained within these.
func unique(f *Function) int64 {
f.uniq++
return f.uniq
}
// exit is a change of control flow going from a range-over-func
// yield function to an ancestor function caused by a break, continue,
// goto, or return statement.
//
// There are 3 types of exits:
// * return from the source function (from ReturnStmt),
// * jump to a block (from break and continue statements [labelled/unlabelled]),
// * go to a label (from goto statements).
//
// As the builder does one pass over the ast, it is unclear whether
// a forward goto statement will leave a range-over-func body.
// The function being exited to is unresolved until the end
// of building the range-over-func body.
type exit struct {
id int64 // unique value for exit within from and to
from *Function // the function the exit starts from
to *Function // the function being exited to (nil if unresolved)
pos token.Pos
block *BasicBlock // basic block within to being jumped to.
label *types.Label // forward label being jumped to via goto.
// block == nil && label == nil => return
}
// storeVar emits to function f code to store a value v to a *types.Var x.
func storeVar(f *Function, x *types.Var, v Value, pos token.Pos) {
emitStore(f, f.lookup(x, true), v, pos)
}
// labelExit creates a new exit to a yield fn to exit the function using a label.
func labelExit(fn *Function, label *types.Label, pos token.Pos) *exit {
e := &exit{
id: unique(fn),
from: fn,
to: nil,
pos: pos,
label: label,
}
fn.exits = append(fn.exits, e)
return e
}
// blockExit creates a new exit to a yield fn that jumps to a basic block.
func blockExit(fn *Function, block *BasicBlock, pos token.Pos) *exit {
e := &exit{
id: unique(fn),
from: fn,
to: block.parent,
pos: pos,
block: block,
}
fn.exits = append(fn.exits, e)
return e
}
// returnExit creates a new exit to a yield fn that returns the source function.
func returnExit(fn *Function, pos token.Pos) *exit {
e := &exit{
id: unique(fn),
from: fn,
to: fn.source,
pos: pos,
}
fn.exits = append(fn.exits, e)
return e
}

127
vendor/golang.org/x/tools/go/ssa/instantiate.go generated vendored Normal file
View File

@@ -0,0 +1,127 @@
// Copyright 2022 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 ssa
import (
"fmt"
"go/types"
"slices"
"sync"
)
// A generic records information about a generic origin function,
// including a cache of existing instantiations.
type generic struct {
instancesMu sync.Mutex
instances map[*typeList]*Function // canonical type arguments to an instance.
}
// instance returns a Function that is the instantiation of generic
// origin function fn with the type arguments targs.
//
// Any created instance is added to cr.
//
// Acquires fn.generic.instancesMu.
func (fn *Function) instance(targs []types.Type, b *builder) *Function {
key := fn.Prog.canon.List(targs)
gen := fn.generic
gen.instancesMu.Lock()
defer gen.instancesMu.Unlock()
inst, ok := gen.instances[key]
if !ok {
inst = createInstance(fn, targs)
inst.buildshared = b.shared()
b.enqueue(inst)
if gen.instances == nil {
gen.instances = make(map[*typeList]*Function)
}
gen.instances[key] = inst
} else {
b.waitForSharedFunction(inst)
}
return inst
}
// createInstance returns the instantiation of generic function fn using targs.
//
// Requires fn.generic.instancesMu.
func createInstance(fn *Function, targs []types.Type) *Function {
prog := fn.Prog
// Compute signature.
var sig *types.Signature
var obj *types.Func
if recv := fn.Signature.Recv(); recv != nil {
// method
obj = prog.canon.instantiateMethod(fn.object, targs, prog.ctxt)
sig = obj.Type().(*types.Signature)
} else {
// function
instSig, err := types.Instantiate(prog.ctxt, fn.Signature, targs, false)
if err != nil {
panic(err)
}
instance, ok := instSig.(*types.Signature)
if !ok {
panic("Instantiate of a Signature returned a non-signature")
}
obj = fn.object // instantiation does not exist yet
sig = prog.canon.Type(instance).(*types.Signature)
}
// Choose strategy (instance or wrapper).
var (
synthetic string
subst *subster
build buildFunc
)
if prog.mode&InstantiateGenerics != 0 && !prog.isParameterized(targs...) {
synthetic = fmt.Sprintf("instance of %s", fn.Name())
if fn.syntax != nil {
subst = makeSubster(prog.ctxt, obj, fn.typeparams, targs)
build = (*builder).buildFromSyntax
} else {
build = (*builder).buildParamsOnly
}
} else {
synthetic = fmt.Sprintf("instantiation wrapper of %s", fn.Name())
build = (*builder).buildInstantiationWrapper
}
/* generic instance or instantiation wrapper */
return &Function{
name: fmt.Sprintf("%s%s", fn.Name(), targs), // may not be unique
object: obj,
Signature: sig,
Synthetic: synthetic,
syntax: fn.syntax, // \
info: fn.info, // } empty for non-created packages
goversion: fn.goversion, // /
build: build,
topLevelOrigin: fn,
pos: obj.Pos(),
Pkg: nil,
Prog: fn.Prog,
typeparams: fn.typeparams, // share with origin
typeargs: targs,
subst: subst,
}
}
// isParameterized reports whether any of the specified types contains
// a free type parameter. It is safe to call concurrently.
func (prog *Program) isParameterized(ts ...types.Type) bool {
prog.hasParamsMu.Lock()
defer prog.hasParamsMu.Unlock()
// TODO(adonovan): profile. If this operation is expensive,
// handle the most common but shallow cases such as T, pkg.T,
// *T without consulting the cache under the lock.
return slices.ContainsFunc(ts, prog.hasParams.Has)
}

671
vendor/golang.org/x/tools/go/ssa/lift.go generated vendored Normal file
View File

@@ -0,0 +1,671 @@
// Copyright 2013 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 ssa
// This file defines the lifting pass which tries to "lift" Alloc
// cells (new/local variables) into SSA registers, replacing loads
// with the dominating stored value, eliminating loads and stores, and
// inserting φ-nodes as needed.
// Cited papers and resources:
//
// Ron Cytron et al. 1991. Efficiently computing SSA form...
// http://doi.acm.org/10.1145/115372.115320
//
// Cooper, Harvey, Kennedy. 2001. A Simple, Fast Dominance Algorithm.
// Software Practice and Experience 2001, 4:1-10.
// http://www.hipersoft.rice.edu/grads/publications/dom14.pdf
//
// Daniel Berlin, llvmdev mailing list, 2012.
// http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-January/046638.html
// (Be sure to expand the whole thread.)
// TODO(adonovan): opt: there are many optimizations worth evaluating, and
// the conventional wisdom for SSA construction is that a simple
// algorithm well engineered often beats those of better asymptotic
// complexity on all but the most egregious inputs.
//
// Danny Berlin suggests that the Cooper et al. algorithm for
// computing the dominance frontier is superior to Cytron et al.
// Furthermore he recommends that rather than computing the DF for the
// whole function then renaming all alloc cells, it may be cheaper to
// compute the DF for each alloc cell separately and throw it away.
//
// Consider exploiting liveness information to avoid creating dead
// φ-nodes which we then immediately remove.
//
// Also see many other "TODO: opt" suggestions in the code.
import (
"fmt"
"go/token"
"math/big"
"os"
"slices"
"golang.org/x/tools/internal/typeparams"
)
// If true, show diagnostic information at each step of lifting.
// Very verbose.
const debugLifting = false
// domFrontier maps each block to the set of blocks in its dominance
// frontier. The outer slice is conceptually a map keyed by
// Block.Index. The inner slice is conceptually a set, possibly
// containing duplicates.
//
// TODO(adonovan): opt: measure impact of dups; consider a packed bit
// representation, e.g. big.Int, and bitwise parallel operations for
// the union step in the Children loop.
//
// domFrontier's methods mutate the slice's elements but not its
// length, so their receivers needn't be pointers.
type domFrontier [][]*BasicBlock
func (df domFrontier) add(u, v *BasicBlock) {
p := &df[u.Index]
*p = append(*p, v)
}
// build builds the dominance frontier df for the dominator (sub)tree
// rooted at u, using the Cytron et al. algorithm.
//
// TODO(adonovan): opt: consider Berlin approach, computing pruned SSA
// by pruning the entire IDF computation, rather than merely pruning
// the DF -> IDF step.
func (df domFrontier) build(u *BasicBlock) {
// Encounter each node u in postorder of dom tree.
for _, child := range u.dom.children {
df.build(child)
}
for _, vb := range u.Succs {
if v := vb.dom; v.idom != u {
df.add(u, vb)
}
}
for _, w := range u.dom.children {
for _, vb := range df[w.Index] {
// TODO(adonovan): opt: use word-parallel bitwise union.
if v := vb.dom; v.idom != u {
df.add(u, vb)
}
}
}
}
func buildDomFrontier(fn *Function) domFrontier {
df := make(domFrontier, len(fn.Blocks))
df.build(fn.Blocks[0])
if fn.Recover != nil {
df.build(fn.Recover)
}
return df
}
func removeInstr(refs []Instruction, instr Instruction) []Instruction {
return slices.DeleteFunc(refs, func(i Instruction) bool { return i == instr })
}
// lift replaces local and new Allocs accessed only with
// load/store by SSA registers, inserting φ-nodes where necessary.
// The result is a program in classical pruned SSA form.
//
// Preconditions:
// - fn has no dead blocks (blockopt has run).
// - Def/use info (Operands and Referrers) is up-to-date.
// - The dominator tree is up-to-date.
func lift(fn *Function) {
// TODO(adonovan): opt: lots of little optimizations may be
// worthwhile here, especially if they cause us to avoid
// buildDomFrontier. For example:
//
// - Alloc never loaded? Eliminate.
// - Alloc never stored? Replace all loads with a zero constant.
// - Alloc stored once? Replace loads with dominating store;
// don't forget that an Alloc is itself an effective store
// of zero.
// - Alloc used only within a single block?
// Use degenerate algorithm avoiding φ-nodes.
// - Consider synergy with scalar replacement of aggregates (SRA).
// e.g. *(&x.f) where x is an Alloc.
// Perhaps we'd get better results if we generated this as x.f
// i.e. Field(x, .f) instead of Load(FieldIndex(x, .f)).
// Unclear.
//
// But we will start with the simplest correct code.
df := buildDomFrontier(fn)
if debugLifting {
title := false
for i, blocks := range df {
if blocks != nil {
if !title {
fmt.Fprintf(os.Stderr, "Dominance frontier of %s:\n", fn)
title = true
}
fmt.Fprintf(os.Stderr, "\t%s: %s\n", fn.Blocks[i], blocks)
}
}
}
newPhis := make(newPhiMap)
// During this pass we will replace some BasicBlock.Instrs
// (allocs, loads and stores) with nil, keeping a count in
// BasicBlock.gaps. At the end we will reset Instrs to the
// concatenation of all non-dead newPhis and non-nil Instrs
// for the block, reusing the original array if space permits.
// While we're here, we also eliminate 'rundefers'
// instructions and ssa:deferstack() in functions that contain no
// 'defer' instructions. For now, we also eliminate
// 's = ssa:deferstack()' calls if s doesn't escape, replacing s
// with nil in Defer{DeferStack: s}. This has the same meaning,
// but allows eliminating the intrinsic function `ssa:deferstack()`
// (unless it is needed due to range-over-func instances). This gives
// ssa users more time to support range-over-func.
usesDefer := false
deferstackAlloc, deferstackCall := deferstackPreamble(fn)
eliminateDeferStack := deferstackAlloc != nil && !deferstackAlloc.Heap
// A counter used to generate ~unique ids for Phi nodes, as an
// aid to debugging. We use large numbers to make them highly
// visible. All nodes are renumbered later.
fresh := 1000
// Determine which allocs we can lift and number them densely.
// The renaming phase uses this numbering for compact maps.
numAllocs := 0
for _, b := range fn.Blocks {
b.gaps = 0
b.rundefers = 0
for _, instr := range b.Instrs {
switch instr := instr.(type) {
case *Alloc:
index := -1
if liftAlloc(df, instr, newPhis, &fresh) {
index = numAllocs
numAllocs++
}
instr.index = index
case *Defer:
usesDefer = true
if eliminateDeferStack {
// Clear DeferStack and remove references to loads
if instr.DeferStack != nil {
if refs := instr.DeferStack.Referrers(); refs != nil {
*refs = removeInstr(*refs, instr)
}
instr.DeferStack = nil
}
}
case *RunDefers:
b.rundefers++
}
}
}
// renaming maps an alloc (keyed by index) to its replacement
// value. Initially the renaming contains nil, signifying the
// zero constant of the appropriate type; we construct the
// Const lazily at most once on each path through the domtree.
// TODO(adonovan): opt: cache per-function not per subtree.
renaming := make([]Value, numAllocs)
// Renaming.
rename(fn.Blocks[0], renaming, newPhis)
// Eliminate dead φ-nodes.
removeDeadPhis(fn.Blocks, newPhis)
// Eliminate ssa:deferstack() call.
if eliminateDeferStack {
b := deferstackCall.block
for i, instr := range b.Instrs {
if instr == deferstackCall {
b.Instrs[i] = nil
b.gaps++
break
}
}
}
// Prepend remaining live φ-nodes to each block.
for _, b := range fn.Blocks {
nps := newPhis[b]
j := len(nps)
rundefersToKill := b.rundefers
if usesDefer {
rundefersToKill = 0
}
if j+b.gaps+rundefersToKill == 0 {
continue // fast path: no new phis or gaps
}
// Compact nps + non-nil Instrs into a new slice.
// TODO(adonovan): opt: compact in situ (rightwards)
// if Instrs has sufficient space or slack.
dst := make([]Instruction, len(b.Instrs)+j-b.gaps-rundefersToKill)
for i, np := range nps {
dst[i] = np.phi
}
for _, instr := range b.Instrs {
if instr == nil {
continue
}
if !usesDefer {
if _, ok := instr.(*RunDefers); ok {
continue
}
}
dst[j] = instr
j++
}
b.Instrs = dst
}
// Remove any fn.Locals that were lifted.
j := 0
for _, l := range fn.Locals {
if l.index < 0 {
fn.Locals[j] = l
j++
}
}
// Nil out fn.Locals[j:] to aid GC.
for i := j; i < len(fn.Locals); i++ {
fn.Locals[i] = nil
}
fn.Locals = fn.Locals[:j]
}
// removeDeadPhis removes φ-nodes not transitively needed by a
// non-Phi, non-DebugRef instruction.
func removeDeadPhis(blocks []*BasicBlock, newPhis newPhiMap) {
// First pass: find the set of "live" φ-nodes: those reachable
// from some non-Phi instruction.
//
// We compute reachability in reverse, starting from each φ,
// rather than forwards, starting from each live non-Phi
// instruction, because this way visits much less of the
// Value graph.
livePhis := make(map[*Phi]bool)
for _, npList := range newPhis {
for _, np := range npList {
phi := np.phi
if !livePhis[phi] && phiHasDirectReferrer(phi) {
markLivePhi(livePhis, phi)
}
}
}
// Existing φ-nodes due to && and || operators
// are all considered live (see Go issue 19622).
for _, b := range blocks {
for _, phi := range b.phis() {
markLivePhi(livePhis, phi.(*Phi))
}
}
// Second pass: eliminate unused phis from newPhis.
for block, npList := range newPhis {
j := 0
for _, np := range npList {
if livePhis[np.phi] {
npList[j] = np
j++
} else {
// discard it, first removing it from referrers
for _, val := range np.phi.Edges {
if refs := val.Referrers(); refs != nil {
*refs = removeInstr(*refs, np.phi)
}
}
np.phi.block = nil
}
}
newPhis[block] = npList[:j]
}
}
// markLivePhi marks phi, and all φ-nodes transitively reachable via
// its Operands, live.
func markLivePhi(livePhis map[*Phi]bool, phi *Phi) {
livePhis[phi] = true
for _, rand := range phi.Operands(nil) {
if q, ok := (*rand).(*Phi); ok {
if !livePhis[q] {
markLivePhi(livePhis, q)
}
}
}
}
// phiHasDirectReferrer reports whether phi is directly referred to by
// a non-Phi instruction. Such instructions are the
// roots of the liveness traversal.
func phiHasDirectReferrer(phi *Phi) bool {
for _, instr := range *phi.Referrers() {
if _, ok := instr.(*Phi); !ok {
return true
}
}
return false
}
type blockSet struct{ big.Int } // (inherit methods from Int)
// add adds b to the set and returns true if the set changed.
func (s *blockSet) add(b *BasicBlock) bool {
i := b.Index
if s.Bit(i) != 0 {
return false
}
s.SetBit(&s.Int, i, 1)
return true
}
// take removes an arbitrary element from a set s and
// returns its index, or returns -1 if empty.
func (s *blockSet) take() int {
l := s.BitLen()
for i := range l {
if s.Bit(i) == 1 {
s.SetBit(&s.Int, i, 0)
return i
}
}
return -1
}
// newPhi is a pair of a newly introduced φ-node and the lifted Alloc
// it replaces.
type newPhi struct {
phi *Phi
alloc *Alloc
}
// newPhiMap records for each basic block, the set of newPhis that
// must be prepended to the block.
type newPhiMap map[*BasicBlock][]newPhi
// liftAlloc determines whether alloc can be lifted into registers,
// and if so, it populates newPhis with all the φ-nodes it may require
// and returns true.
//
// fresh is a source of fresh ids for phi nodes.
func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool {
// Don't lift result values in functions that defer
// calls that may recover from panic.
if fn := alloc.Parent(); fn.Recover != nil {
if slices.Contains(fn.results, alloc) {
return false
}
}
// Compute defblocks, the set of blocks containing a
// definition of the alloc cell.
var defblocks blockSet
for _, instr := range *alloc.Referrers() {
// Bail out if we discover the alloc is not liftable;
// the only operations permitted to use the alloc are
// loads/stores into the cell, and DebugRef.
switch instr := instr.(type) {
case *Store:
if instr.Val == alloc {
return false // address used as value
}
if instr.Addr != alloc {
panic("Alloc.Referrers is inconsistent")
}
defblocks.add(instr.Block())
case *UnOp:
if instr.Op != token.MUL {
return false // not a load
}
if instr.X != alloc {
panic("Alloc.Referrers is inconsistent")
}
case *DebugRef:
// ok
default:
return false // some other instruction
}
}
// The Alloc itself counts as a (zero) definition of the cell.
defblocks.add(alloc.Block())
if debugLifting {
fmt.Fprintln(os.Stderr, "\tlifting ", alloc, alloc.Name())
}
fn := alloc.Parent()
// Φ-insertion.
//
// What follows is the body of the main loop of the insert-φ
// function described by Cytron et al, but instead of using
// counter tricks, we just reset the 'hasAlready' and 'work'
// sets each iteration. These are bitmaps so it's pretty cheap.
//
// TODO(adonovan): opt: recycle slice storage for W,
// hasAlready, defBlocks across liftAlloc calls.
var hasAlready blockSet
// Initialize W and work to defblocks.
var work blockSet = defblocks // blocks seen
var W blockSet // blocks to do
W.Set(&defblocks.Int)
// Traverse iterated dominance frontier, inserting φ-nodes.
for i := W.take(); i != -1; i = W.take() {
u := fn.Blocks[i]
for _, v := range df[u.Index] {
if hasAlready.add(v) {
// Create φ-node.
// It will be prepended to v.Instrs later, if needed.
phi := &Phi{
Edges: make([]Value, len(v.Preds)),
Comment: alloc.Comment,
}
// This is merely a debugging aid:
phi.setNum(*fresh)
*fresh++
phi.pos = alloc.Pos()
phi.setType(typeparams.MustDeref(alloc.Type()))
phi.block = v
if debugLifting {
fmt.Fprintf(os.Stderr, "\tplace %s = %s at block %s\n", phi.Name(), phi, v)
}
newPhis[v] = append(newPhis[v], newPhi{phi, alloc})
if work.add(v) {
W.add(v)
}
}
}
}
return true
}
// replaceAll replaces all intraprocedural uses of x with y,
// updating x.Referrers and y.Referrers.
// Precondition: x.Referrers() != nil, i.e. x must be local to some function.
func replaceAll(x, y Value) {
var rands []*Value
pxrefs := x.Referrers()
pyrefs := y.Referrers()
for _, instr := range *pxrefs {
rands = instr.Operands(rands[:0]) // recycle storage
for _, rand := range rands {
if *rand != nil {
if *rand == x {
*rand = y
}
}
}
if pyrefs != nil {
*pyrefs = append(*pyrefs, instr) // dups ok
}
}
*pxrefs = nil // x is now unreferenced
}
// renamed returns the value to which alloc is being renamed,
// constructing it lazily if it's the implicit zero initialization.
func renamed(renaming []Value, alloc *Alloc) Value {
v := renaming[alloc.index]
if v == nil {
v = zeroConst(typeparams.MustDeref(alloc.Type()))
renaming[alloc.index] = v
}
return v
}
// rename implements the (Cytron et al) SSA renaming algorithm, a
// preorder traversal of the dominator tree replacing all loads of
// Alloc cells with the value stored to that cell by the dominating
// store instruction. For lifting, we need only consider loads,
// stores and φ-nodes.
//
// renaming is a map from *Alloc (keyed by index number) to its
// dominating stored value; newPhis[x] is the set of new φ-nodes to be
// prepended to block x.
func rename(u *BasicBlock, renaming []Value, newPhis newPhiMap) {
// Each φ-node becomes the new name for its associated Alloc.
for _, np := range newPhis[u] {
phi := np.phi
alloc := np.alloc
renaming[alloc.index] = phi
}
// Rename loads and stores of allocs.
for i, instr := range u.Instrs {
switch instr := instr.(type) {
case *Alloc:
if instr.index >= 0 { // store of zero to Alloc cell
// Replace dominated loads by the zero value.
renaming[instr.index] = nil
if debugLifting {
fmt.Fprintf(os.Stderr, "\tkill alloc %s\n", instr)
}
// Delete the Alloc.
u.Instrs[i] = nil
u.gaps++
}
case *Store:
if alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 { // store to Alloc cell
// Replace dominated loads by the stored value.
renaming[alloc.index] = instr.Val
if debugLifting {
fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n",
instr, instr.Val.Name())
}
// Remove the store from the referrer list of the stored value.
if refs := instr.Val.Referrers(); refs != nil {
*refs = removeInstr(*refs, instr)
}
// Delete the Store.
u.Instrs[i] = nil
u.gaps++
}
case *UnOp:
if instr.Op == token.MUL {
if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // load of Alloc cell
newval := renamed(renaming, alloc)
if debugLifting {
fmt.Fprintf(os.Stderr, "\tupdate load %s = %s with %s\n",
instr.Name(), instr, newval.Name())
}
// Replace all references to
// the loaded value by the
// dominating stored value.
replaceAll(instr, newval)
// Delete the Load.
u.Instrs[i] = nil
u.gaps++
}
}
case *DebugRef:
if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // ref of Alloc cell
if instr.IsAddr {
instr.X = renamed(renaming, alloc)
instr.IsAddr = false
// Add DebugRef to instr.X's referrers.
if refs := instr.X.Referrers(); refs != nil {
*refs = append(*refs, instr)
}
} else {
// A source expression denotes the address
// of an Alloc that was optimized away.
instr.X = nil
// Delete the DebugRef.
u.Instrs[i] = nil
u.gaps++
}
}
}
}
// For each φ-node in a CFG successor, rename the edge.
for _, v := range u.Succs {
phis := newPhis[v]
if len(phis) == 0 {
continue
}
i := v.predIndex(u)
for _, np := range phis {
phi := np.phi
alloc := np.alloc
newval := renamed(renaming, alloc)
if debugLifting {
fmt.Fprintf(os.Stderr, "\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\n",
phi.Name(), u, v, i, alloc.Name(), newval.Name())
}
phi.Edges[i] = newval
if prefs := newval.Referrers(); prefs != nil {
*prefs = append(*prefs, phi)
}
}
}
// Continue depth-first recursion over domtree, pushing a
// fresh copy of the renaming map for each subtree.
for i, v := range u.dom.children {
r := renaming
if i < len(u.dom.children)-1 {
// On all but the final iteration, we must make
// a copy to avoid destructive update.
r = make([]Value, len(renaming))
copy(r, renaming)
}
rename(v, r, newPhis)
}
}
// deferstackPreamble returns the *Alloc and ssa:deferstack() call for fn.deferstack.
func deferstackPreamble(fn *Function) (*Alloc, *Call) {
if alloc, _ := fn.vars[fn.deferstack].(*Alloc); alloc != nil {
for _, ref := range *alloc.Referrers() {
if ref, _ := ref.(*Store); ref != nil && ref.Addr == alloc {
if call, _ := ref.Val.(*Call); call != nil {
return alloc, call
}
}
}
}
return nil, nil
}

155
vendor/golang.org/x/tools/go/ssa/lvalue.go generated vendored Normal file
View File

@@ -0,0 +1,155 @@
// Copyright 2013 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 ssa
// lvalues are the union of addressable expressions and map-index
// expressions.
import (
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/internal/typeparams"
)
// An lvalue represents an assignable location that may appear on the
// left-hand side of an assignment. This is a generalization of a
// pointer to permit updates to elements of maps.
type lvalue interface {
store(fn *Function, v Value) // stores v into the location
load(fn *Function) Value // loads the contents of the location
address(fn *Function) Value // address of the location
typ() types.Type // returns the type of the location
}
// An address is an lvalue represented by a true pointer.
type address struct {
addr Value // must have a pointer core type.
pos token.Pos // source position
expr ast.Expr // source syntax of the value (not address) [debug mode]
}
func (a *address) load(fn *Function) Value {
load := emitLoad(fn, a.addr)
load.pos = a.pos
return load
}
func (a *address) store(fn *Function, v Value) {
store := emitStore(fn, a.addr, v, a.pos)
if a.expr != nil {
// store.Val is v, converted for assignability.
emitDebugRef(fn, a.expr, store.Val, false)
}
}
func (a *address) address(fn *Function) Value {
if a.expr != nil {
emitDebugRef(fn, a.expr, a.addr, true)
}
return a.addr
}
func (a *address) typ() types.Type {
return typeparams.MustDeref(a.addr.Type())
}
// An element is an lvalue represented by m[k], the location of an
// element of a map. These locations are not addressable
// since pointers cannot be formed from them, but they do support
// load() and store().
type element struct {
m, k Value // map
t types.Type // map element type
pos token.Pos // source position of colon ({k:v}) or lbrack (m[k]=v)
}
func (e *element) load(fn *Function) Value {
l := &Lookup{
X: e.m,
Index: e.k,
}
l.setPos(e.pos)
l.setType(e.t)
return fn.emit(l)
}
func (e *element) store(fn *Function, v Value) {
up := &MapUpdate{
Map: e.m,
Key: e.k,
Value: emitConv(fn, v, e.t),
}
up.pos = e.pos
fn.emit(up)
}
func (e *element) address(fn *Function) Value {
panic("map elements are not addressable")
}
func (e *element) typ() types.Type {
return e.t
}
// A lazyAddress is an lvalue whose address is the result of an instruction.
// These work like an *address except a new address.address() Value
// is created on each load, store and address call.
// A lazyAddress can be used to control when a side effect (nil pointer
// dereference, index out of bounds) of using a location happens.
type lazyAddress struct {
addr func(fn *Function) Value // emit to fn the computation of the address
t types.Type // type of the location
pos token.Pos // source position
expr ast.Expr // source syntax of the value (not address) [debug mode]
}
func (l *lazyAddress) load(fn *Function) Value {
load := emitLoad(fn, l.addr(fn))
load.pos = l.pos
return load
}
func (l *lazyAddress) store(fn *Function, v Value) {
store := emitStore(fn, l.addr(fn), v, l.pos)
if l.expr != nil {
// store.Val is v, converted for assignability.
emitDebugRef(fn, l.expr, store.Val, false)
}
}
func (l *lazyAddress) address(fn *Function) Value {
addr := l.addr(fn)
if l.expr != nil {
emitDebugRef(fn, l.expr, addr, true)
}
return addr
}
func (l *lazyAddress) typ() types.Type { return l.t }
// A blank is a dummy variable whose name is "_".
// It is not reified: loads are illegal and stores are ignored.
type blank struct{}
func (bl blank) load(fn *Function) Value {
panic("blank.load is illegal")
}
func (bl blank) store(fn *Function, v Value) {
// no-op
}
func (bl blank) address(fn *Function) Value {
panic("blank var is not addressable")
}
func (bl blank) typ() types.Type {
// This should be the type of the blank Ident; the typechecker
// doesn't provide this yet, but fortunately, we don't need it
// yet either.
panic("blank.typ is unimplemented")
}

180
vendor/golang.org/x/tools/go/ssa/methods.go generated vendored Normal file
View File

@@ -0,0 +1,180 @@
// Copyright 2013 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 ssa
// This file defines utilities for population of method sets.
import (
"fmt"
"go/types"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typesinternal"
)
// MethodValue returns the Function implementing method sel, building
// wrapper methods on demand. It returns nil if sel denotes an
// interface or generic method.
//
// Precondition: sel.Kind() == MethodVal.
//
// Thread-safe.
//
// Acquires prog.methodsMu.
func (prog *Program) MethodValue(sel *types.Selection) *Function {
if sel.Kind() != types.MethodVal {
panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel))
}
T := sel.Recv()
if types.IsInterface(T) {
return nil // interface method or type parameter
}
if prog.isParameterized(T) {
return nil // generic method
}
if prog.mode&LogSource != 0 {
defer logStack("MethodValue %s %v", T, sel)()
}
var b builder
m := func() *Function {
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
// Get or create SSA method set.
mset, ok := prog.methodSets.At(T).(*methodSet)
if !ok {
mset = &methodSet{mapping: make(map[string]*Function)}
prog.methodSets.Set(T, mset)
}
// Get or create SSA method.
id := sel.Obj().Id()
fn, ok := mset.mapping[id]
if !ok {
obj := sel.Obj().(*types.Func)
needsPromotion := len(sel.Index()) > 1
needsIndirection := !isPointer(recvType(obj)) && isPointer(T)
if needsPromotion || needsIndirection {
fn = createWrapper(prog, toSelection(sel))
fn.buildshared = b.shared()
b.enqueue(fn)
} else {
fn = prog.objectMethod(obj, &b)
}
if fn.Signature.Recv() == nil {
panic(fn)
}
mset.mapping[id] = fn
} else {
b.waitForSharedFunction(fn)
}
return fn
}()
b.iterate()
return m
}
// objectMethod returns the Function for a given method symbol.
// The symbol may be an instance of a generic function. It need not
// belong to an existing SSA package created by a call to
// prog.CreatePackage.
//
// objectMethod panics if the function is not a method.
//
// Acquires prog.objectMethodsMu.
func (prog *Program) objectMethod(obj *types.Func, b *builder) *Function {
sig := obj.Type().(*types.Signature)
if sig.Recv() == nil {
panic("not a method: " + obj.String())
}
// Belongs to a created package?
if fn := prog.FuncValue(obj); fn != nil {
return fn
}
// Instantiation of generic?
if originObj := obj.Origin(); originObj != obj {
origin := prog.objectMethod(originObj, b)
assert(origin.typeparams.Len() > 0, "origin is not generic")
targs := receiverTypeArgs(obj)
return origin.instance(targs, b)
}
// Consult/update cache of methods created from types.Func.
prog.objectMethodsMu.Lock()
defer prog.objectMethodsMu.Unlock()
fn, ok := prog.objectMethods[obj]
if !ok {
fn = createFunction(prog, obj, obj.Name(), nil, nil, "")
fn.Synthetic = "from type information (on demand)"
fn.buildshared = b.shared()
b.enqueue(fn)
if prog.objectMethods == nil {
prog.objectMethods = make(map[*types.Func]*Function)
}
prog.objectMethods[obj] = fn
} else {
b.waitForSharedFunction(fn)
}
return fn
}
// LookupMethod returns the implementation of the method of type T
// identified by (pkg, name). It returns nil if the method exists but
// is an interface method or generic method, and panics if T has no such method.
func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
if sel == nil {
panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
}
return prog.MethodValue(sel)
}
// methodSet contains the (concrete) methods of a concrete type (non-interface, non-parameterized).
type methodSet struct {
mapping map[string]*Function // populated lazily
}
// RuntimeTypes returns a new unordered slice containing all types in
// the program for which a runtime type is required.
//
// A runtime type is required for any non-parameterized, non-interface
// type that is converted to an interface, or for any type (including
// interface types) derivable from one through reflection.
//
// The methods of such types may be reachable through reflection or
// interface calls even if they are never called directly.
//
// Thread-safe.
//
// Acquires prog.makeInterfaceTypesMu.
func (prog *Program) RuntimeTypes() []types.Type {
prog.makeInterfaceTypesMu.Lock()
defer prog.makeInterfaceTypesMu.Unlock()
// Compute the derived types on demand, since many SSA clients
// never call RuntimeTypes, and those that do typically call
// it once (often within ssautil.AllFunctions, which will
// eventually not use it; see Go issue #69291.) This
// eliminates the need to eagerly compute all the element
// types during SSA building.
var runtimeTypes []types.Type
add := func(t types.Type) { runtimeTypes = append(runtimeTypes, t) }
var set typeutil.Map // for de-duping identical types
for t := range prog.makeInterfaceTypes {
typesinternal.ForEachElement(&set, &prog.MethodSets, t, add)
}
return runtimeTypes
}

111
vendor/golang.org/x/tools/go/ssa/mode.go generated vendored Normal file
View File

@@ -0,0 +1,111 @@
// 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.
package ssa
// This file defines the BuilderMode type and its command-line flag.
import (
"bytes"
"fmt"
)
// BuilderMode is a bitmask of options for diagnostics and checking.
//
// *BuilderMode satisfies the flag.Value interface. Example:
//
// var mode = ssa.BuilderMode(0)
// func init() { flag.Var(&mode, "build", ssa.BuilderModeDoc) }
type BuilderMode uint
const (
PrintPackages BuilderMode = 1 << iota // Print package inventory to stdout
PrintFunctions // Print function SSA code to stdout
LogSource // Log source locations as SSA builder progresses
SanityCheckFunctions // Perform sanity checking of function bodies
NaiveForm // Build naïve SSA form: don't replace local loads/stores with registers
BuildSerially // Build packages serially, not in parallel.
GlobalDebug // Enable debug info for all packages
BareInits // Build init functions without guards or calls to dependent inits
InstantiateGenerics // Instantiate generics functions (monomorphize) while building
)
const BuilderModeDoc = `Options controlling the SSA builder.
The value is a sequence of zero or more of these letters:
C perform sanity [C]hecking of the SSA form.
D include [D]ebug info for every function.
P print [P]ackage inventory.
F print [F]unction SSA code.
S log [S]ource locations as SSA builder progresses.
L build distinct packages seria[L]ly instead of in parallel.
N build [N]aive SSA form: don't replace local loads/stores with registers.
I build bare [I]nit functions: no init guards or calls to dependent inits.
G instantiate [G]eneric function bodies via monomorphization
`
func (m BuilderMode) String() string {
var buf bytes.Buffer
if m&GlobalDebug != 0 {
buf.WriteByte('D')
}
if m&PrintPackages != 0 {
buf.WriteByte('P')
}
if m&PrintFunctions != 0 {
buf.WriteByte('F')
}
if m&LogSource != 0 {
buf.WriteByte('S')
}
if m&SanityCheckFunctions != 0 {
buf.WriteByte('C')
}
if m&NaiveForm != 0 {
buf.WriteByte('N')
}
if m&BuildSerially != 0 {
buf.WriteByte('L')
}
if m&BareInits != 0 {
buf.WriteByte('I')
}
if m&InstantiateGenerics != 0 {
buf.WriteByte('G')
}
return buf.String()
}
// Set parses the flag characters in s and updates *m.
func (m *BuilderMode) Set(s string) error {
var mode BuilderMode
for _, c := range s {
switch c {
case 'D':
mode |= GlobalDebug
case 'P':
mode |= PrintPackages
case 'F':
mode |= PrintFunctions
case 'S':
mode |= LogSource | BuildSerially
case 'C':
mode |= SanityCheckFunctions
case 'N':
mode |= NaiveForm
case 'L':
mode |= BuildSerially
case 'I':
mode |= BareInits
case 'G':
mode |= InstantiateGenerics
default:
return fmt.Errorf("unknown BuilderMode option: %q", c)
}
}
*m = mode
return nil
}
// Get returns m.
func (m BuilderMode) Get() any { return m }

462
vendor/golang.org/x/tools/go/ssa/print.go generated vendored Normal file
View File

@@ -0,0 +1,462 @@
// Copyright 2013 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 ssa
// This file implements the String() methods for all Value and
// Instruction types.
import (
"bytes"
"fmt"
"go/types"
"io"
"reflect"
"sort"
"strings"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typeparams"
)
// relName returns the name of v relative to i.
// In most cases, this is identical to v.Name(), but references to
// Functions (including methods) and Globals use RelString and
// all types are displayed with relType, so that only cross-package
// references are package-qualified.
func relName(v Value, i Instruction) string {
var from *types.Package
if i != nil {
from = i.Parent().relPkg()
}
switch v := v.(type) {
case Member: // *Function or *Global
return v.RelString(from)
case *Const:
return v.RelString(from)
}
return v.Name()
}
func relType(t types.Type, from *types.Package) string {
return types.TypeString(t, types.RelativeTo(from))
}
func relTerm(term *types.Term, from *types.Package) string {
s := relType(term.Type(), from)
if term.Tilde() {
return "~" + s
}
return s
}
func relString(m Member, from *types.Package) string {
// NB: not all globals have an Object (e.g. init$guard),
// so use Package().Object not Object.Package().
if pkg := m.Package().Pkg; pkg != nil && pkg != from {
return fmt.Sprintf("%s.%s", pkg.Path(), m.Name())
}
return m.Name()
}
// Value.String()
//
// This method is provided only for debugging.
// It never appears in disassembly, which uses Value.Name().
func (v *Parameter) String() string {
from := v.Parent().relPkg()
return fmt.Sprintf("parameter %s : %s", v.Name(), relType(v.Type(), from))
}
func (v *FreeVar) String() string {
from := v.Parent().relPkg()
return fmt.Sprintf("freevar %s : %s", v.Name(), relType(v.Type(), from))
}
func (v *Builtin) String() string {
return fmt.Sprintf("builtin %s", v.Name())
}
// Instruction.String()
func (v *Alloc) String() string {
op := "local"
if v.Heap {
op = "new"
}
from := v.Parent().relPkg()
return fmt.Sprintf("%s %s (%s)", op, relType(typeparams.MustDeref(v.Type()), from), v.Comment)
}
func (v *Phi) String() string {
var b bytes.Buffer
b.WriteString("phi [")
for i, edge := range v.Edges {
if i > 0 {
b.WriteString(", ")
}
// Be robust against malformed CFG.
if v.block == nil {
b.WriteString("??")
continue
}
block := -1
if i < len(v.block.Preds) {
block = v.block.Preds[i].Index
}
fmt.Fprintf(&b, "%d: ", block)
edgeVal := "<nil>" // be robust
if edge != nil {
edgeVal = relName(edge, v)
}
b.WriteString(edgeVal)
}
b.WriteString("]")
if v.Comment != "" {
b.WriteString(" #")
b.WriteString(v.Comment)
}
return b.String()
}
func printCall(v *CallCommon, prefix string, instr Instruction) string {
var b bytes.Buffer
b.WriteString(prefix)
if !v.IsInvoke() {
b.WriteString(relName(v.Value, instr))
} else {
fmt.Fprintf(&b, "invoke %s.%s", relName(v.Value, instr), v.Method.Name())
}
b.WriteString("(")
for i, arg := range v.Args {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(relName(arg, instr))
}
if v.Signature().Variadic() {
b.WriteString("...")
}
b.WriteString(")")
return b.String()
}
func (c *CallCommon) String() string {
return printCall(c, "", nil)
}
func (v *Call) String() string {
return printCall(&v.Call, "", v)
}
func (v *BinOp) String() string {
return fmt.Sprintf("%s %s %s", relName(v.X, v), v.Op.String(), relName(v.Y, v))
}
func (v *UnOp) String() string {
return fmt.Sprintf("%s%s%s", v.Op, relName(v.X, v), commaOk(v.CommaOk))
}
func printConv(prefix string, v, x Value) string {
from := v.Parent().relPkg()
return fmt.Sprintf("%s %s <- %s (%s)",
prefix,
relType(v.Type(), from),
relType(x.Type(), from),
relName(x, v.(Instruction)))
}
func (v *ChangeType) String() string { return printConv("changetype", v, v.X) }
func (v *Convert) String() string { return printConv("convert", v, v.X) }
func (v *ChangeInterface) String() string { return printConv("change interface", v, v.X) }
func (v *SliceToArrayPointer) String() string { return printConv("slice to array pointer", v, v.X) }
func (v *MakeInterface) String() string { return printConv("make", v, v.X) }
func (v *MultiConvert) String() string {
from := v.Parent().relPkg()
var b strings.Builder
b.WriteString(printConv("multiconvert", v, v.X))
b.WriteString(" [")
for i, s := range termListOf(v.from) {
for j, d := range termListOf(v.to) {
if i != 0 || j != 0 {
b.WriteString(" | ")
}
fmt.Fprintf(&b, "%s <- %s", relTerm(d, from), relTerm(s, from))
}
}
b.WriteString("]")
return b.String()
}
func (v *MakeClosure) String() string {
var b bytes.Buffer
fmt.Fprintf(&b, "make closure %s", relName(v.Fn, v))
if v.Bindings != nil {
b.WriteString(" [")
for i, c := range v.Bindings {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(relName(c, v))
}
b.WriteString("]")
}
return b.String()
}
func (v *MakeSlice) String() string {
from := v.Parent().relPkg()
return fmt.Sprintf("make %s %s %s",
relType(v.Type(), from),
relName(v.Len, v),
relName(v.Cap, v))
}
func (v *Slice) String() string {
var b bytes.Buffer
b.WriteString("slice ")
b.WriteString(relName(v.X, v))
b.WriteString("[")
if v.Low != nil {
b.WriteString(relName(v.Low, v))
}
b.WriteString(":")
if v.High != nil {
b.WriteString(relName(v.High, v))
}
if v.Max != nil {
b.WriteString(":")
b.WriteString(relName(v.Max, v))
}
b.WriteString("]")
return b.String()
}
func (v *MakeMap) String() string {
res := ""
if v.Reserve != nil {
res = relName(v.Reserve, v)
}
from := v.Parent().relPkg()
return fmt.Sprintf("make %s %s", relType(v.Type(), from), res)
}
func (v *MakeChan) String() string {
from := v.Parent().relPkg()
return fmt.Sprintf("make %s %s", relType(v.Type(), from), relName(v.Size, v))
}
func (v *FieldAddr) String() string {
// Be robust against a bad index.
name := "?"
if fld := fieldOf(typeparams.MustDeref(v.X.Type()), v.Field); fld != nil {
name = fld.Name()
}
return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field)
}
func (v *Field) String() string {
// Be robust against a bad index.
name := "?"
if fld := fieldOf(v.X.Type(), v.Field); fld != nil {
name = fld.Name()
}
return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field)
}
func (v *IndexAddr) String() string {
return fmt.Sprintf("&%s[%s]", relName(v.X, v), relName(v.Index, v))
}
func (v *Index) String() string {
return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v))
}
func (v *Lookup) String() string {
return fmt.Sprintf("%s[%s]%s", relName(v.X, v), relName(v.Index, v), commaOk(v.CommaOk))
}
func (v *Range) String() string {
return "range " + relName(v.X, v)
}
func (v *Next) String() string {
return "next " + relName(v.Iter, v)
}
func (v *TypeAssert) String() string {
from := v.Parent().relPkg()
return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from))
}
func (v *Extract) String() string {
return fmt.Sprintf("extract %s #%d", relName(v.Tuple, v), v.Index)
}
func (s *Jump) String() string {
// Be robust against malformed CFG.
block := -1
if s.block != nil && len(s.block.Succs) == 1 {
block = s.block.Succs[0].Index
}
return fmt.Sprintf("jump %d", block)
}
func (s *If) String() string {
// Be robust against malformed CFG.
tblock, fblock := -1, -1
if s.block != nil && len(s.block.Succs) == 2 {
tblock = s.block.Succs[0].Index
fblock = s.block.Succs[1].Index
}
return fmt.Sprintf("if %s goto %d else %d", relName(s.Cond, s), tblock, fblock)
}
func (s *Go) String() string {
return printCall(&s.Call, "go ", s)
}
func (s *Panic) String() string {
return "panic " + relName(s.X, s)
}
func (s *Return) String() string {
var b bytes.Buffer
b.WriteString("return")
for i, r := range s.Results {
if i == 0 {
b.WriteString(" ")
} else {
b.WriteString(", ")
}
b.WriteString(relName(r, s))
}
return b.String()
}
func (*RunDefers) String() string {
return "rundefers"
}
func (s *Send) String() string {
return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s))
}
func (s *Defer) String() string {
prefix := "defer "
if s.DeferStack != nil {
prefix += "[" + relName(s.DeferStack, s) + "] "
}
c := printCall(&s.Call, prefix, s)
return c
}
func (s *Select) String() string {
var b bytes.Buffer
for i, st := range s.States {
if i > 0 {
b.WriteString(", ")
}
if st.Dir == types.RecvOnly {
b.WriteString("<-")
b.WriteString(relName(st.Chan, s))
} else {
b.WriteString(relName(st.Chan, s))
b.WriteString("<-")
b.WriteString(relName(st.Send, s))
}
}
non := ""
if !s.Blocking {
non = "non"
}
return fmt.Sprintf("select %sblocking [%s]", non, b.String())
}
func (s *Store) String() string {
return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s))
}
func (s *MapUpdate) String() string {
return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s))
}
func (s *DebugRef) String() string {
p := s.Parent().Prog.Fset.Position(s.Pos())
var descr any
if s.object != nil {
descr = s.object // e.g. "var x int"
} else {
descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr"
}
var addr string
if s.IsAddr {
addr = "address of "
}
return fmt.Sprintf("; %s%s @ %d:%d is %s", addr, descr, p.Line, p.Column, s.X.Name())
}
func (p *Package) String() string {
return "package " + p.Pkg.Path()
}
var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer
func (p *Package) WriteTo(w io.Writer) (int64, error) {
var buf bytes.Buffer
WritePackage(&buf, p)
n, err := w.Write(buf.Bytes())
return int64(n), err
}
// WritePackage writes to buf a human-readable summary of p.
func WritePackage(buf *bytes.Buffer, p *Package) {
fmt.Fprintf(buf, "%s:\n", p)
var names []string
maxname := 0
for name := range p.Members {
if l := len(name); l > maxname {
maxname = l
}
names = append(names, name)
}
from := p.Pkg
sort.Strings(names)
for _, name := range names {
switch mem := p.Members[name].(type) {
case *NamedConst:
fmt.Fprintf(buf, " const %-*s %s = %s\n",
maxname, name, mem.Name(), mem.Value.RelString(from))
case *Function:
fmt.Fprintf(buf, " func %-*s %s\n",
maxname, name, relType(mem.Type(), from))
case *Type:
fmt.Fprintf(buf, " type %-*s %s\n",
maxname, name, relType(mem.Type().Underlying(), from))
for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from)))
}
case *Global:
fmt.Fprintf(buf, " var %-*s %s\n",
maxname, name, relType(typeparams.MustDeref(mem.Type()), from))
}
}
fmt.Fprintf(buf, "\n")
}
func commaOk(x bool) string {
if x {
return ",ok"
}
return ""
}

637
vendor/golang.org/x/tools/go/ssa/sanity.go generated vendored Normal file
View File

@@ -0,0 +1,637 @@
// Copyright 2013 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 ssa
// An optional pass for sanity-checking invariants of the SSA representation.
// Currently it checks CFG invariants but little at the instruction level.
import (
"bytes"
"fmt"
"go/ast"
"go/types"
"io"
"os"
"slices"
"strings"
)
type sanity struct {
reporter io.Writer
fn *Function
block *BasicBlock
instrs map[Instruction]unit
insane bool
}
// sanityCheck performs integrity checking of the SSA representation
// of the function fn (which must have been "built") and returns true
// if it was valid. Diagnostics are written to reporter if non-nil,
// os.Stderr otherwise. Some diagnostics are only warnings and do not
// imply a negative result.
//
// Sanity-checking is intended to facilitate the debugging of code
// transformation passes.
func sanityCheck(fn *Function, reporter io.Writer) bool {
if reporter == nil {
reporter = os.Stderr
}
return (&sanity{reporter: reporter}).checkFunction(fn)
}
// mustSanityCheck is like sanityCheck but panics instead of returning
// a negative result.
func mustSanityCheck(fn *Function, reporter io.Writer) {
if !sanityCheck(fn, reporter) {
fn.WriteTo(os.Stderr)
panic("SanityCheck failed")
}
}
func (s *sanity) diagnostic(prefix, format string, args ...any) {
fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn)
if s.block != nil {
fmt.Fprintf(s.reporter, ", block %s", s.block)
}
io.WriteString(s.reporter, ": ")
fmt.Fprintf(s.reporter, format, args...)
io.WriteString(s.reporter, "\n")
}
func (s *sanity) errorf(format string, args ...any) {
s.insane = true
s.diagnostic("Error", format, args...)
}
func (s *sanity) warnf(format string, args ...any) {
s.diagnostic("Warning", format, args...)
}
// findDuplicate returns an arbitrary basic block that appeared more
// than once in blocks, or nil if all were unique.
func findDuplicate(blocks []*BasicBlock) *BasicBlock {
if len(blocks) < 2 {
return nil
}
if blocks[0] == blocks[1] {
return blocks[0]
}
// Slow path:
m := make(map[*BasicBlock]bool)
for _, b := range blocks {
if m[b] {
return b
}
m[b] = true
}
return nil
}
func (s *sanity) checkInstr(idx int, instr Instruction) {
switch instr := instr.(type) {
case *If, *Jump, *Return, *Panic:
s.errorf("control flow instruction not at end of block")
case *Phi:
if idx == 0 {
// It suffices to apply this check to just the first phi node.
if dup := findDuplicate(s.block.Preds); dup != nil {
s.errorf("phi node in block with duplicate predecessor %s", dup)
}
} else {
prev := s.block.Instrs[idx-1]
if _, ok := prev.(*Phi); !ok {
s.errorf("Phi instruction follows a non-Phi: %T", prev)
}
}
if ne, np := len(instr.Edges), len(s.block.Preds); ne != np {
s.errorf("phi node has %d edges but %d predecessors", ne, np)
} else {
for i, e := range instr.Edges {
if e == nil {
s.errorf("phi node '%s' has no value for edge #%d from %s", instr.Comment, i, s.block.Preds[i])
} else if !types.Identical(instr.typ, e.Type()) {
s.errorf("phi node '%s' has a different type (%s) for edge #%d from %s (%s)",
instr.Comment, instr.Type(), i, s.block.Preds[i], e.Type())
}
}
}
case *Alloc:
if !instr.Heap {
found := slices.Contains(s.fn.Locals, instr)
if !found {
s.errorf("local alloc %s = %s does not appear in Function.Locals", instr.Name(), instr)
}
}
case *BinOp:
case *Call:
if common := instr.Call; common.IsInvoke() {
if !types.IsInterface(common.Value.Type()) {
s.errorf("invoke on %s (%s) which is not an interface type (or type param)", common.Value, common.Value.Type())
}
}
case *ChangeInterface:
case *ChangeType:
case *SliceToArrayPointer:
case *Convert:
if from := instr.X.Type(); !isBasicConvTypes(from) {
if to := instr.Type(); !isBasicConvTypes(to) {
s.errorf("convert %s -> %s: at least one type must be basic (or all basic, []byte, or []rune)", from, to)
}
}
case *MultiConvert:
case *Defer:
case *Extract:
case *Field:
case *FieldAddr:
case *Go:
case *Index:
case *IndexAddr:
case *Lookup:
case *MakeChan:
case *MakeClosure:
numFree := len(instr.Fn.(*Function).FreeVars)
numBind := len(instr.Bindings)
if numFree != numBind {
s.errorf("MakeClosure has %d Bindings for function %s with %d free vars",
numBind, instr.Fn, numFree)
}
if recv := instr.Type().(*types.Signature).Recv(); recv != nil {
s.errorf("MakeClosure's type includes receiver %s", recv.Type())
}
case *MakeInterface:
case *MakeMap:
case *MakeSlice:
case *MapUpdate:
case *Next:
case *Range:
case *RunDefers:
case *Select:
case *Send:
case *Slice:
case *Store:
case *TypeAssert:
case *UnOp:
case *DebugRef:
// TODO(adonovan): implement checks.
default:
panic(fmt.Sprintf("Unknown instruction type: %T", instr))
}
if call, ok := instr.(CallInstruction); ok {
if call.Common().Signature() == nil {
s.errorf("nil signature: %s", call)
}
}
// Check that value-defining instructions have valid types
// and a valid referrer list.
if v, ok := instr.(Value); ok {
t := v.Type()
if t == nil {
s.errorf("no type: %s = %s", v.Name(), v)
} else if t == tRangeIter || t == tDeferStack {
// not a proper type; ignore.
} else if b, ok := t.Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
s.errorf("instruction has 'untyped' result: %s = %s : %s", v.Name(), v, t)
}
s.checkReferrerList(v)
}
// Untyped constants are legal as instruction Operands(),
// for example:
// _ = "foo"[0]
// or:
// if wordsize==64 {...}
// All other non-Instruction Values can be found via their
// enclosing Function or Package.
}
func (s *sanity) checkFinalInstr(instr Instruction) {
switch instr := instr.(type) {
case *If:
if nsuccs := len(s.block.Succs); nsuccs != 2 {
s.errorf("If-terminated block has %d successors; expected 2", nsuccs)
return
}
if s.block.Succs[0] == s.block.Succs[1] {
s.errorf("If-instruction has same True, False target blocks: %s", s.block.Succs[0])
return
}
case *Jump:
if nsuccs := len(s.block.Succs); nsuccs != 1 {
s.errorf("Jump-terminated block has %d successors; expected 1", nsuccs)
return
}
case *Return:
if nsuccs := len(s.block.Succs); nsuccs != 0 {
s.errorf("Return-terminated block has %d successors; expected none", nsuccs)
return
}
if na, nf := len(instr.Results), s.fn.Signature.Results().Len(); nf != na {
s.errorf("%d-ary return in %d-ary function", na, nf)
}
case *Panic:
if nsuccs := len(s.block.Succs); nsuccs != 0 {
s.errorf("Panic-terminated block has %d successors; expected none", nsuccs)
return
}
default:
s.errorf("non-control flow instruction at end of block")
}
}
func (s *sanity) checkBlock(b *BasicBlock, index int) {
s.block = b
if b.Index != index {
s.errorf("block has incorrect Index %d", b.Index)
}
if b.parent != s.fn {
s.errorf("block has incorrect parent %s", b.parent)
}
// Check all blocks are reachable.
// (The entry block is always implicitly reachable,
// as is the Recover block, if any.)
if (index > 0 && b != b.parent.Recover) && len(b.Preds) == 0 {
s.warnf("unreachable block")
if b.Instrs == nil {
// Since this block is about to be pruned,
// tolerating transient problems in it
// simplifies other optimizations.
return
}
}
// Check predecessor and successor relations are dual,
// and that all blocks in CFG belong to same function.
for _, a := range b.Preds {
found := slices.Contains(a.Succs, b)
if !found {
s.errorf("expected successor edge in predecessor %s; found only: %s", a, a.Succs)
}
if a.parent != s.fn {
s.errorf("predecessor %s belongs to different function %s", a, a.parent)
}
}
for _, c := range b.Succs {
found := slices.Contains(c.Preds, b)
if !found {
s.errorf("expected predecessor edge in successor %s; found only: %s", c, c.Preds)
}
if c.parent != s.fn {
s.errorf("successor %s belongs to different function %s", c, c.parent)
}
}
// Check each instruction is sane.
n := len(b.Instrs)
if n == 0 {
s.errorf("basic block contains no instructions")
}
var rands [10]*Value // reuse storage
for j, instr := range b.Instrs {
if instr == nil {
s.errorf("nil instruction at index %d", j)
continue
}
if b2 := instr.Block(); b2 == nil {
s.errorf("nil Block() for instruction at index %d", j)
continue
} else if b2 != b {
s.errorf("wrong Block() (%s) for instruction at index %d ", b2, j)
continue
}
if j < n-1 {
s.checkInstr(j, instr)
} else {
s.checkFinalInstr(instr)
}
// Check Instruction.Operands.
operands:
for i, op := range instr.Operands(rands[:0]) {
if op == nil {
s.errorf("nil operand pointer %d of %s", i, instr)
continue
}
val := *op
if val == nil {
continue // a nil operand is ok
}
// Check that "untyped" types only appear on constant operands.
if _, ok := (*op).(*Const); !ok {
if basic, ok := (*op).Type().Underlying().(*types.Basic); ok {
if basic.Info()&types.IsUntyped != 0 {
s.errorf("operand #%d of %s is untyped: %s", i, instr, basic)
}
}
}
// Check that Operands that are also Instructions belong to same function.
// TODO(adonovan): also check their block dominates block b.
if val, ok := val.(Instruction); ok {
if val.Block() == nil {
s.errorf("operand %d of %s is an instruction (%s) that belongs to no block", i, instr, val)
} else if val.Parent() != s.fn {
s.errorf("operand %d of %s is an instruction (%s) from function %s", i, instr, val, val.Parent())
}
}
// Check that each function-local operand of
// instr refers back to instr. (NB: quadratic)
switch val := val.(type) {
case *Const, *Global, *Builtin:
continue // not local
case *Function:
if val.parent == nil {
continue // only anon functions are local
}
}
// TODO(adonovan): check val.Parent() != nil <=> val.Referrers() is defined.
if refs := val.Referrers(); refs != nil {
for _, ref := range *refs {
if ref == instr {
continue operands
}
}
s.errorf("operand %d of %s (%s) does not refer to us", i, instr, val)
} else {
s.errorf("operand %d of %s (%s) has no referrers", i, instr, val)
}
}
}
}
func (s *sanity) checkReferrerList(v Value) {
refs := v.Referrers()
if refs == nil {
s.errorf("%s has missing referrer list", v.Name())
return
}
for i, ref := range *refs {
if _, ok := s.instrs[ref]; !ok {
s.errorf("%s.Referrers()[%d] = %s is not an instruction belonging to this function", v.Name(), i, ref)
}
}
}
func (s *sanity) checkFunctionParams() {
signature := s.fn.Signature
params := s.fn.Params
// startSigParams is the start of signature.Params() within params.
startSigParams := 0
if signature.Recv() != nil {
startSigParams = 1
}
if startSigParams+signature.Params().Len() != len(params) {
s.errorf("function has %d parameters in signature but has %d after building",
startSigParams+signature.Params().Len(), len(params))
return
}
for i, param := range params {
var sigType types.Type
si := i - startSigParams
if si < 0 {
sigType = signature.Recv().Type()
} else {
sigType = signature.Params().At(si).Type()
}
if !types.Identical(sigType, param.Type()) {
s.errorf("expect type %s in signature but got type %s in param %d", param.Type(), sigType, i)
}
}
}
// checkTransientFields checks whether all transient fields of Function are cleared.
func (s *sanity) checkTransientFields() {
fn := s.fn
if fn.build != nil {
s.errorf("function transient field 'build' is not nil")
}
if fn.currentBlock != nil {
s.errorf("function transient field 'currentBlock' is not nil")
}
if fn.vars != nil {
s.errorf("function transient field 'vars' is not nil")
}
if fn.results != nil {
s.errorf("function transient field 'results' is not nil")
}
if fn.returnVars != nil {
s.errorf("function transient field 'returnVars' is not nil")
}
if fn.targets != nil {
s.errorf("function transient field 'targets' is not nil")
}
if fn.lblocks != nil {
s.errorf("function transient field 'lblocks' is not nil")
}
if fn.subst != nil {
s.errorf("function transient field 'subst' is not nil")
}
if fn.jump != nil {
s.errorf("function transient field 'jump' is not nil")
}
if fn.deferstack != nil {
s.errorf("function transient field 'deferstack' is not nil")
}
if fn.source != nil {
s.errorf("function transient field 'source' is not nil")
}
if fn.exits != nil {
s.errorf("function transient field 'exits' is not nil")
}
if fn.uniq != 0 {
s.errorf("function transient field 'uniq' is not zero")
}
}
func (s *sanity) checkFunction(fn *Function) bool {
s.fn = fn
s.checkFunctionParams()
s.checkTransientFields()
// TODO(taking): Sanity check origin, typeparams, and typeargs.
if fn.Prog == nil {
s.errorf("nil Prog")
}
var buf bytes.Buffer
_ = fn.String() // must not crash
_ = fn.RelString(fn.relPkg()) // must not crash
WriteFunction(&buf, fn) // must not crash
// All functions have a package, except delegates (which are
// shared across packages, or duplicated as weak symbols in a
// separate-compilation model), and error.Error.
if fn.Pkg == nil {
if strings.HasPrefix(fn.Synthetic, "from type information (on demand)") ||
strings.HasPrefix(fn.Synthetic, "wrapper ") ||
strings.HasPrefix(fn.Synthetic, "bound ") ||
strings.HasPrefix(fn.Synthetic, "thunk ") ||
strings.HasSuffix(fn.name, "Error") ||
strings.HasPrefix(fn.Synthetic, "instance ") ||
strings.HasPrefix(fn.Synthetic, "instantiation ") ||
(fn.parent != nil && len(fn.typeargs) > 0) /* anon fun in instance */ {
// ok
} else {
s.errorf("nil Pkg")
}
}
if src, syn := fn.Synthetic == "", fn.Syntax() != nil; src != syn {
if len(fn.typeargs) > 0 && fn.Prog.mode&InstantiateGenerics != 0 {
// ok (instantiation with InstantiateGenerics on)
} else if fn.topLevelOrigin != nil && len(fn.typeargs) > 0 {
// ok (we always have the syntax set for instantiation)
} else if _, rng := fn.syntax.(*ast.RangeStmt); rng && fn.Synthetic == "range-over-func yield" {
// ok (range-func-yields are both synthetic and keep syntax)
} else {
s.errorf("got fromSource=%t, hasSyntax=%t; want same values", src, syn)
}
}
// Build the set of valid referrers.
s.instrs = make(map[Instruction]unit)
// instrs are the instructions that are present in the function.
for instr := range fn.instrs() {
s.instrs[instr] = unit{}
}
// Check all Locals allocations appear in the function instruction.
for i, l := range fn.Locals {
if _, present := s.instrs[l]; !present {
s.warnf("function doesn't contain Local alloc %s", l.Name())
}
if l.Parent() != fn {
s.errorf("Local %s at index %d has wrong parent", l.Name(), i)
}
if l.Heap {
s.errorf("Local %s at index %d has Heap flag set", l.Name(), i)
}
}
for i, p := range fn.Params {
if p.Parent() != fn {
s.errorf("Param %s at index %d has wrong parent", p.Name(), i)
}
// Check common suffix of Signature and Params match type.
if sig := fn.Signature; sig != nil {
j := i - len(fn.Params) + sig.Params().Len() // index within sig.Params
if j < 0 {
continue
}
if !types.Identical(p.Type(), sig.Params().At(j).Type()) {
s.errorf("Param %s at index %d has wrong type (%s, versus %s in Signature)", p.Name(), i, p.Type(), sig.Params().At(j).Type())
}
}
s.checkReferrerList(p)
}
for i, fv := range fn.FreeVars {
if fv.Parent() != fn {
s.errorf("FreeVar %s at index %d has wrong parent", fv.Name(), i)
}
s.checkReferrerList(fv)
}
if fn.Blocks != nil && len(fn.Blocks) == 0 {
// Function _had_ blocks (so it's not external) but
// they were "optimized" away, even the entry block.
s.errorf("Blocks slice is non-nil but empty")
}
for i, b := range fn.Blocks {
if b == nil {
s.warnf("nil *BasicBlock at f.Blocks[%d]", i)
continue
}
s.checkBlock(b, i)
}
if fn.Recover != nil && fn.Blocks[fn.Recover.Index] != fn.Recover {
s.errorf("Recover block is not in Blocks slice")
}
s.block = nil
for i, anon := range fn.AnonFuncs {
if anon.Parent() != fn {
s.errorf("AnonFuncs[%d]=%s but %s.Parent()=%s", i, anon, anon, anon.Parent())
}
if i != int(anon.anonIdx) {
s.errorf("AnonFuncs[%d]=%s but %s.anonIdx=%d", i, anon, anon, anon.anonIdx)
}
}
s.fn = nil
return !s.insane
}
// sanityCheckPackage checks invariants of packages upon creation.
// It does not require that the package is built.
// Unlike sanityCheck (for functions), it just panics at the first error.
func sanityCheckPackage(pkg *Package) {
if pkg.Pkg == nil {
panic(fmt.Sprintf("Package %s has no Object", pkg))
}
if pkg.info != nil {
panic(fmt.Sprintf("package %s field 'info' is not cleared", pkg))
}
if pkg.files != nil {
panic(fmt.Sprintf("package %s field 'files' is not cleared", pkg))
}
if pkg.created != nil {
panic(fmt.Sprintf("package %s field 'created' is not cleared", pkg))
}
if pkg.initVersion != nil {
panic(fmt.Sprintf("package %s field 'initVersion' is not cleared", pkg))
}
_ = pkg.String() // must not crash
for name, mem := range pkg.Members {
if name != mem.Name() {
panic(fmt.Sprintf("%s: %T.Name() = %s, want %s",
pkg.Pkg.Path(), mem, mem.Name(), name))
}
obj := mem.Object()
if obj == nil {
// This check is sound because fields
// {Global,Function}.object have type
// types.Object. (If they were declared as
// *types.{Var,Func}, we'd have a non-empty
// interface containing a nil pointer.)
continue // not all members have typechecker objects
}
if obj.Name() != name {
if obj.Name() == "init" && strings.HasPrefix(mem.Name(), "init#") {
// Ok. The name of a declared init function varies between
// its types.Func ("init") and its ssa.Function ("init#%d").
} else {
panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s",
pkg.Pkg.Path(), mem, obj.Name(), name))
}
}
if obj.Pos() != mem.Pos() {
panic(fmt.Sprintf("%s Pos=%d obj.Pos=%d", mem, mem.Pos(), obj.Pos()))
}
}
}

288
vendor/golang.org/x/tools/go/ssa/source.go generated vendored Normal file
View File

@@ -0,0 +1,288 @@
// Copyright 2013 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 ssa
// This file defines utilities for working with source positions
// or source-level named entities ("objects").
// TODO(adonovan): test that {Value,Instruction}.Pos() positions match
// the originating syntax, as specified.
import (
"go/ast"
"go/token"
"go/types"
)
// EnclosingFunction returns the function that contains the syntax
// node denoted by path.
//
// Syntax associated with package-level variable specifications is
// enclosed by the package's init() function.
//
// Returns nil if not found; reasons might include:
// - the node is not enclosed by any function.
// - the node is within an anonymous function (FuncLit) and
// its SSA function has not been created yet
// (pkg.Build() has not yet been called).
func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
// Start with package-level function...
fn := findEnclosingPackageLevelFunction(pkg, path)
if fn == nil {
return nil // not in any function
}
// ...then walk down the nested anonymous functions.
n := len(path)
outer:
for i := range path {
if lit, ok := path[n-1-i].(*ast.FuncLit); ok {
for _, anon := range fn.AnonFuncs {
if anon.Pos() == lit.Type.Func {
fn = anon
continue outer
}
}
// SSA function not found:
// - package not yet built, or maybe
// - builder skipped FuncLit in dead block
// (in principle; but currently the Builder
// generates even dead FuncLits).
return nil
}
}
return fn
}
// HasEnclosingFunction returns true if the AST node denoted by path
// is contained within the declaration of some function or
// package-level variable.
//
// Unlike EnclosingFunction, the behaviour of this function does not
// depend on whether SSA code for pkg has been built, so it can be
// used to quickly reject check inputs that will cause
// EnclosingFunction to fail, prior to SSA building.
func HasEnclosingFunction(pkg *Package, path []ast.Node) bool {
return findEnclosingPackageLevelFunction(pkg, path) != nil
}
// findEnclosingPackageLevelFunction returns the Function
// corresponding to the package-level function enclosing path.
func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function {
if n := len(path); n >= 2 { // [... {Gen,Func}Decl File]
switch decl := path[n-2].(type) {
case *ast.GenDecl:
if decl.Tok == token.VAR && n >= 3 {
// Package-level 'var' initializer.
return pkg.init
}
case *ast.FuncDecl:
if decl.Recv == nil && decl.Name.Name == "init" {
// Explicit init() function.
for _, b := range pkg.init.Blocks {
for _, instr := range b.Instrs {
if instr, ok := instr.(*Call); ok {
if callee, ok := instr.Call.Value.(*Function); ok && callee.Pkg == pkg && callee.Pos() == decl.Name.NamePos {
return callee
}
}
}
}
// Hack: return non-nil when SSA is not yet
// built so that HasEnclosingFunction works.
return pkg.init
}
// Declared function/method.
return findNamedFunc(pkg, decl.Name.NamePos)
}
}
return nil // not in any function
}
// findNamedFunc returns the named function whose FuncDecl.Ident is at
// position pos.
func findNamedFunc(pkg *Package, pos token.Pos) *Function {
// Look at all package members and method sets of named types.
// Not very efficient.
for _, mem := range pkg.Members {
switch mem := mem.(type) {
case *Function:
if mem.Pos() == pos {
return mem
}
case *Type:
mset := pkg.Prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
for i, n := 0, mset.Len(); i < n; i++ {
// Don't call Program.Method: avoid creating wrappers.
obj := mset.At(i).Obj().(*types.Func)
if obj.Pos() == pos {
// obj from MethodSet may not be the origin type.
m := obj.Origin()
return pkg.objects[m].(*Function)
}
}
}
}
return nil
}
// ValueForExpr returns the SSA Value that corresponds to non-constant
// expression e.
//
// It returns nil if no value was found, e.g.
// - the expression is not lexically contained within f;
// - f was not built with debug information; or
// - e is a constant expression. (For efficiency, no debug
// information is stored for constants. Use
// go/types.Info.Types[e].Value instead.)
// - e is a reference to nil or a built-in function.
// - the value was optimised away.
//
// If e is an addressable expression used in an lvalue context,
// value is the address denoted by e, and isAddr is true.
//
// The types of e (or &e, if isAddr) and the result are equal
// (modulo "untyped" bools resulting from comparisons).
//
// (Tip: to find the ssa.Value given a source position, use
// astutil.PathEnclosingInterval to locate the ast.Node, then
// EnclosingFunction to locate the Function, then ValueForExpr to find
// the ssa.Value.)
func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {
if f.debugInfo() { // (opt)
e = ast.Unparen(e)
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
if ref, ok := instr.(*DebugRef); ok {
if ref.Expr == e {
return ref.X, ref.IsAddr
}
}
}
}
}
return
}
// --- Lookup functions for source-level named entities (types.Objects) ---
// Package returns the SSA Package corresponding to the specified
// type-checker package. It returns nil if no such Package was
// created by a prior call to prog.CreatePackage.
func (prog *Program) Package(pkg *types.Package) *Package {
return prog.packages[pkg]
}
// packageLevelMember returns the package-level member corresponding
// to the specified symbol, which may be a package-level const
// (*NamedConst), var (*Global) or func/method (*Function) of some
// package in prog.
//
// It returns nil if the object belongs to a package that has not been
// created by prog.CreatePackage.
func (prog *Program) packageLevelMember(obj types.Object) Member {
if pkg, ok := prog.packages[obj.Pkg()]; ok {
return pkg.objects[obj]
}
return nil
}
// FuncValue returns the SSA function or (non-interface) method
// denoted by the specified func symbol. It returns nil if the symbol
// denotes an interface method, or belongs to a package that was not
// created by prog.CreatePackage.
func (prog *Program) FuncValue(obj *types.Func) *Function {
fn, _ := prog.packageLevelMember(obj).(*Function)
return fn
}
// ConstValue returns the SSA constant denoted by the specified const symbol.
func (prog *Program) ConstValue(obj *types.Const) *Const {
// TODO(adonovan): opt: share (don't reallocate)
// Consts for const objects and constant ast.Exprs.
// Universal constant? {true,false,nil}
if obj.Parent() == types.Universe {
return NewConst(obj.Val(), obj.Type())
}
// Package-level named constant?
if v := prog.packageLevelMember(obj); v != nil {
return v.(*NamedConst).Value
}
return NewConst(obj.Val(), obj.Type())
}
// VarValue returns the SSA Value that corresponds to a specific
// identifier denoting the specified var symbol.
//
// VarValue returns nil if a local variable was not found, perhaps
// because its package was not built, the debug information was not
// requested during SSA construction, or the value was optimized away.
//
// ref is the path to an ast.Ident (e.g. from PathEnclosingInterval),
// and that ident must resolve to obj.
//
// pkg is the package enclosing the reference. (A reference to a var
// always occurs within a function, so we need to know where to find it.)
//
// If the identifier is a field selector and its base expression is
// non-addressable, then VarValue returns the value of that field.
// For example:
//
// func f() struct {x int}
// f().x // VarValue(x) returns a *Field instruction of type int
//
// All other identifiers denote addressable locations (variables).
// For them, VarValue may return either the variable's address or its
// value, even when the expression is evaluated only for its value; the
// situation is reported by isAddr, the second component of the result.
//
// If !isAddr, the returned value is the one associated with the
// specific identifier. For example,
//
// var x int // VarValue(x) returns Const 0 here
// x = 1 // VarValue(x) returns Const 1 here
//
// It is not specified whether the value or the address is returned in
// any particular case, as it may depend upon optimizations performed
// during SSA code generation, such as registerization, constant
// folding, avoidance of materialization of subexpressions, etc.
func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) {
// All references to a var are local to some function, possibly init.
fn := EnclosingFunction(pkg, ref)
if fn == nil {
return // e.g. def of struct field; SSA not built?
}
id := ref[0].(*ast.Ident)
// Defining ident of a parameter?
if id.Pos() == obj.Pos() {
for _, param := range fn.Params {
if param.Object() == obj {
return param, false
}
}
}
// Other ident?
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
if dr, ok := instr.(*DebugRef); ok {
if dr.Pos() == id.Pos() {
return dr.X, dr.IsAddr
}
}
}
}
// Defining ident of package-level var?
if v := prog.packageLevelMember(obj); v != nil {
return v.(*Global), true
}
return // e.g. debug info not requested, or var optimized away
}

1873
vendor/golang.org/x/tools/go/ssa/ssa.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

36
vendor/golang.org/x/tools/go/ssa/ssautil/deprecated.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
// 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.
package ssautil
// This file contains deprecated public APIs.
// We discourage their use.
import (
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
)
// CreateProgram returns a new program in SSA form, given a program
// loaded from source. An SSA package is created for each transitively
// error-free package of lprog.
//
// Code for bodies of functions is not built until Build is called
// on the result.
//
// The mode parameter controls diagnostics and checking during SSA construction.
//
// Deprecated: Use [golang.org/x/tools/go/packages] and the [Packages]
// function instead; see ssa.Example_loadPackages.
func CreateProgram(lprog *loader.Program, mode ssa.BuilderMode) *ssa.Program {
prog := ssa.NewProgram(lprog.Fset, mode)
for _, info := range lprog.AllPackages {
if info.TransitivelyErrorFree {
prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
}
}
return prog
}

189
vendor/golang.org/x/tools/go/ssa/ssautil/load.go generated vendored Normal file
View File

@@ -0,0 +1,189 @@
// 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.
package ssautil
// This file defines utility functions for constructing programs in SSA form.
import (
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
)
// Packages creates an SSA program for a set of packages.
//
// The packages must have been loaded from source syntax using the
// [packages.Load] function in [packages.LoadSyntax] or
// [packages.LoadAllSyntax] mode.
//
// Packages creates an SSA package for each well-typed package in the
// initial list, plus all their dependencies. The resulting list of
// packages corresponds to the list of initial packages, and may contain
// a nil if SSA code could not be constructed for the corresponding initial
// package due to type errors.
//
// Code for bodies of functions is not built until [Program.Build] is
// called on the resulting Program. SSA code is constructed only for
// the initial packages with well-typed syntax trees.
//
// The mode parameter controls diagnostics and checking during SSA construction.
func Packages(initial []*packages.Package, mode ssa.BuilderMode) (*ssa.Program, []*ssa.Package) {
// TODO(adonovan): opt: this calls CreatePackage far more than
// necessary: for all dependencies, not just the (non-initial)
// direct dependencies of the initial packages.
//
// But can it reasonably be changed without breaking the
// spirit and/or letter of the law above? Clients may notice
// if we call CreatePackage less, as methods like
// Program.FuncValue will return nil. Or must we provide a new
// function (and perhaps deprecate this one)? Is it worth it?
//
// Tim King makes the interesting point that it would be
// possible to entirely alleviate the client from the burden
// of calling CreatePackage for non-syntax packages, if we
// were to treat vars and funcs lazily in the same way we now
// treat methods. (In essence, try to move away from the
// notion of ssa.Packages, and make the Program answer
// all reasonable questions about any types.Object.)
return doPackages(initial, mode, false)
}
// AllPackages creates an SSA program for a set of packages plus all
// their dependencies.
//
// The packages must have been loaded from source syntax using the
// [packages.Load] function in [packages.LoadAllSyntax] mode.
//
// AllPackages creates an SSA package for each well-typed package in the
// initial list, plus all their dependencies. The resulting list of
// packages corresponds to the list of initial packages, and may contain
// a nil if SSA code could not be constructed for the corresponding
// initial package due to type errors.
//
// Code for bodies of functions is not built until Build is called on
// the resulting Program. SSA code is constructed for all packages with
// well-typed syntax trees.
//
// The mode parameter controls diagnostics and checking during SSA construction.
func AllPackages(initial []*packages.Package, mode ssa.BuilderMode) (*ssa.Program, []*ssa.Package) {
return doPackages(initial, mode, true)
}
func doPackages(initial []*packages.Package, mode ssa.BuilderMode, deps bool) (*ssa.Program, []*ssa.Package) {
var fset *token.FileSet
if len(initial) > 0 {
fset = initial[0].Fset
}
prog := ssa.NewProgram(fset, mode)
isInitial := make(map[*packages.Package]bool, len(initial))
for _, p := range initial {
isInitial[p] = true
}
ssamap := make(map[*packages.Package]*ssa.Package)
packages.Visit(initial, nil, func(p *packages.Package) {
if p.Types != nil && !p.IllTyped {
var files []*ast.File
var info *types.Info
if deps || isInitial[p] {
files = p.Syntax
info = p.TypesInfo
}
ssamap[p] = prog.CreatePackage(p.Types, files, info, true)
}
})
var ssapkgs []*ssa.Package
for _, p := range initial {
ssapkgs = append(ssapkgs, ssamap[p]) // may be nil
}
return prog, ssapkgs
}
// BuildPackage builds an SSA program with SSA intermediate
// representation (IR) for all functions of a single package.
//
// It populates pkg by type-checking the specified file syntax trees. All
// dependencies are loaded using the importer specified by tc, which
// typically loads compiler export data; SSA code cannot be built for
// those packages. BuildPackage then constructs an [ssa.Program] with all
// dependency packages created, and builds and returns the SSA package
// corresponding to pkg.
//
// The caller must have set pkg.Path to the import path.
//
// The operation fails if there were any type-checking or import errors.
//
// See ../example_test.go for an example.
func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) {
if fset == nil {
panic("no token.FileSet")
}
if pkg.Path() == "" {
panic("package has no import path")
}
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Instances: make(map[*ast.Ident]types.Instance),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
FileVersions: make(map[*ast.File]string),
}
if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil {
return nil, nil, err
}
prog := ssa.NewProgram(fset, mode)
// Create SSA packages for all imports.
// Order is not significant.
created := make(map[*types.Package]bool)
var createAll func(pkgs []*types.Package)
createAll = func(pkgs []*types.Package) {
for _, p := range pkgs {
if !created[p] {
created[p] = true
prog.CreatePackage(p, nil, nil, true)
createAll(p.Imports())
}
}
}
createAll(pkg.Imports())
// TODO(adonovan): we could replace createAll with just:
//
// // Create SSA packages for all imports.
// for _, p := range pkg.Imports() {
// prog.CreatePackage(p, nil, nil, true)
// }
//
// (with minor changes to changes to ../builder_test.go as
// shown in CL 511715 PS 10.) But this would strictly violate
// the letter of the doc comment above, which says "all
// dependencies created".
//
// Tim makes the good point with some extra work we could
// remove the need for any CreatePackage calls except the
// ones with syntax (i.e. primary packages). Of course
// You wouldn't have ssa.Packages and Members for as
// many things but no-one really uses that anyway.
// I wish I had done this from the outset.
// Create and build the primary package.
ssapkg := prog.CreatePackage(pkg, files, info, false)
ssapkg.Build()
return ssapkg, info, nil
}

230
vendor/golang.org/x/tools/go/ssa/ssautil/switch.go generated vendored Normal file
View File

@@ -0,0 +1,230 @@
// Copyright 2013 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 ssautil
// This file implements discovery of switch and type-switch constructs
// from low-level control flow.
//
// Many techniques exist for compiling a high-level switch with
// constant cases to efficient machine code. The optimal choice will
// depend on the data type, the specific case values, the code in the
// body of each case, and the hardware.
// Some examples:
// - a lookup table (for a switch that maps constants to constants)
// - a computed goto
// - a binary tree
// - a perfect hash
// - a two-level switch (to partition constant strings by their first byte).
import (
"bytes"
"fmt"
"go/token"
"go/types"
"golang.org/x/tools/go/ssa"
)
// A ConstCase represents a single constant comparison.
// It is part of a Switch.
type ConstCase struct {
Block *ssa.BasicBlock // block performing the comparison
Body *ssa.BasicBlock // body of the case
Value *ssa.Const // case comparand
}
// A TypeCase represents a single type assertion.
// It is part of a Switch.
type TypeCase struct {
Block *ssa.BasicBlock // block performing the type assert
Body *ssa.BasicBlock // body of the case
Type types.Type // case type
Binding ssa.Value // value bound by this case
}
// A Switch is a logical high-level control flow operation
// (a multiway branch) discovered by analysis of a CFG containing
// only if/else chains. It is not part of the ssa.Instruction set.
//
// One of ConstCases and TypeCases has length >= 2;
// the other is nil.
//
// In a value switch, the list of cases may contain duplicate constants.
// A type switch may contain duplicate types, or types assignable
// to an interface type also in the list.
// TODO(adonovan): eliminate such duplicates.
type Switch struct {
Start *ssa.BasicBlock // block containing start of if/else chain
X ssa.Value // the switch operand
ConstCases []ConstCase // ordered list of constant comparisons
TypeCases []TypeCase // ordered list of type assertions
Default *ssa.BasicBlock // successor if all comparisons fail
}
func (sw *Switch) String() string {
// We represent each block by the String() of its
// first Instruction, e.g. "print(42:int)".
var buf bytes.Buffer
if sw.ConstCases != nil {
fmt.Fprintf(&buf, "switch %s {\n", sw.X.Name())
for _, c := range sw.ConstCases {
fmt.Fprintf(&buf, "case %s: %s\n", c.Value, c.Body.Instrs[0])
}
} else {
fmt.Fprintf(&buf, "switch %s.(type) {\n", sw.X.Name())
for _, c := range sw.TypeCases {
fmt.Fprintf(&buf, "case %s %s: %s\n",
c.Binding.Name(), c.Type, c.Body.Instrs[0])
}
}
if sw.Default != nil {
fmt.Fprintf(&buf, "default: %s\n", sw.Default.Instrs[0])
}
fmt.Fprintf(&buf, "}")
return buf.String()
}
// Switches examines the control-flow graph of fn and returns the
// set of inferred value and type switches. A value switch tests an
// ssa.Value for equality against two or more compile-time constant
// values. Switches involving link-time constants (addresses) are
// ignored. A type switch type-asserts an ssa.Value against two or
// more types.
//
// The switches are returned in dominance order.
//
// The resulting switches do not necessarily correspond to uses of the
// 'switch' keyword in the source: for example, a single source-level
// switch statement with non-constant cases may result in zero, one or
// many Switches, one per plural sequence of constant cases.
// Switches may even be inferred from if/else- or goto-based control flow.
// (In general, the control flow constructs of the source program
// cannot be faithfully reproduced from the SSA representation.)
func Switches(fn *ssa.Function) []Switch {
// Traverse the CFG in dominance order, so we don't
// enter an if/else-chain in the middle.
var switches []Switch
seen := make(map[*ssa.BasicBlock]bool) // TODO(adonovan): opt: use ssa.blockSet
for _, b := range fn.DomPreorder() {
if x, k := isComparisonBlock(b); x != nil {
// Block b starts a switch.
sw := Switch{Start: b, X: x}
valueSwitch(&sw, k, seen)
if len(sw.ConstCases) > 1 {
switches = append(switches, sw)
}
}
if y, x, T := isTypeAssertBlock(b); y != nil {
// Block b starts a type switch.
sw := Switch{Start: b, X: x}
typeSwitch(&sw, y, T, seen)
if len(sw.TypeCases) > 1 {
switches = append(switches, sw)
}
}
}
return switches
}
func valueSwitch(sw *Switch, k *ssa.Const, seen map[*ssa.BasicBlock]bool) {
b := sw.Start
x := sw.X
for x == sw.X {
if seen[b] {
break
}
seen[b] = true
sw.ConstCases = append(sw.ConstCases, ConstCase{
Block: b,
Body: b.Succs[0],
Value: k,
})
b = b.Succs[1]
if len(b.Instrs) > 2 {
// Block b contains not just 'if x == k',
// so it may have side effects that
// make it unsafe to elide.
break
}
if len(b.Preds) != 1 {
// Block b has multiple predecessors,
// so it cannot be treated as a case.
break
}
x, k = isComparisonBlock(b)
}
sw.Default = b
}
func typeSwitch(sw *Switch, y ssa.Value, T types.Type, seen map[*ssa.BasicBlock]bool) {
b := sw.Start
x := sw.X
for x == sw.X {
if seen[b] {
break
}
seen[b] = true
sw.TypeCases = append(sw.TypeCases, TypeCase{
Block: b,
Body: b.Succs[0],
Type: T,
Binding: y,
})
b = b.Succs[1]
if len(b.Instrs) > 4 {
// Block b contains not just
// {TypeAssert; Extract #0; Extract #1; If}
// so it may have side effects that
// make it unsafe to elide.
break
}
if len(b.Preds) != 1 {
// Block b has multiple predecessors,
// so it cannot be treated as a case.
break
}
y, x, T = isTypeAssertBlock(b)
}
sw.Default = b
}
// isComparisonBlock returns the operands (v, k) if a block ends with
// a comparison v==k, where k is a compile-time constant.
func isComparisonBlock(b *ssa.BasicBlock) (v ssa.Value, k *ssa.Const) {
if n := len(b.Instrs); n >= 2 {
if i, ok := b.Instrs[n-1].(*ssa.If); ok {
if binop, ok := i.Cond.(*ssa.BinOp); ok && binop.Block() == b && binop.Op == token.EQL {
if k, ok := binop.Y.(*ssa.Const); ok {
return binop.X, k
}
if k, ok := binop.X.(*ssa.Const); ok {
return binop.Y, k
}
}
}
}
return
}
// isTypeAssertBlock returns the operands (y, x, T) if a block ends with
// a type assertion "if y, ok := x.(T); ok {".
func isTypeAssertBlock(b *ssa.BasicBlock) (y, x ssa.Value, T types.Type) {
if n := len(b.Instrs); n >= 4 {
if i, ok := b.Instrs[n-1].(*ssa.If); ok {
if ext1, ok := i.Cond.(*ssa.Extract); ok && ext1.Block() == b && ext1.Index == 1 {
if ta, ok := ext1.Tuple.(*ssa.TypeAssert); ok && ta.Block() == b {
// hack: relies upon instruction ordering.
if ext0, ok := b.Instrs[n-3].(*ssa.Extract); ok {
return ext0, ta.X, ta.AssertedType
}
}
}
}
}
return
}

157
vendor/golang.org/x/tools/go/ssa/ssautil/visit.go generated vendored Normal file
View File

@@ -0,0 +1,157 @@
// Copyright 2013 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 ssautil // import "golang.org/x/tools/go/ssa/ssautil"
import (
"go/ast"
"go/types"
"golang.org/x/tools/go/ssa"
_ "unsafe" // for linkname hack
)
// This file defines utilities for visiting the SSA representation of
// a Program.
//
// TODO(adonovan): test coverage.
// AllFunctions finds and returns the set of functions potentially
// needed by program prog, as determined by a simple linker-style
// reachability algorithm starting from the members and method-sets of
// each package. The result may include anonymous functions and
// synthetic wrappers.
//
// Precondition: all packages are built.
//
// TODO(adonovan): this function is underspecified. It doesn't
// actually work like a linker, which computes reachability from main
// using something like go/callgraph/cha (without materializing the
// call graph). In fact, it treats all public functions and all
// methods of public non-parameterized types as roots, even though
// they may be unreachable--but only in packages created from syntax.
//
// I think we should deprecate AllFunctions function in favor of two
// clearly defined ones:
//
// 1. The first would efficiently compute CHA reachability from a set
// of main packages, making it suitable for a whole-program
// analysis context with InstantiateGenerics, in conjunction with
// Program.Build.
//
// 2. The second would return only the set of functions corresponding
// to source Func{Decl,Lit} syntax, like SrcFunctions in
// go/analysis/passes/buildssa; this is suitable for
// package-at-a-time (or handful of packages) context.
// ssa.Package could easily expose it as a field.
//
// We could add them unexported for now and use them via the linkname hack.
func AllFunctions(prog *ssa.Program) map[*ssa.Function]bool {
seen := make(map[*ssa.Function]bool)
var function func(fn *ssa.Function)
function = func(fn *ssa.Function) {
if !seen[fn] {
seen[fn] = true
var buf [10]*ssa.Value // avoid alloc in common case
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
for _, op := range instr.Operands(buf[:0]) {
if fn, ok := (*op).(*ssa.Function); ok {
function(fn)
}
}
}
}
}
}
// TODO(adonovan): opt: provide a way to share a builder
// across a sequence of MethodValue calls.
methodsOf := func(T types.Type) {
if !types.IsInterface(T) {
mset := prog.MethodSets.MethodSet(T)
for method := range mset.Methods() {
function(prog.MethodValue(method))
}
}
}
// Historically, Program.RuntimeTypes used to include the type
// of any exported member of a package loaded from syntax that
// has a non-parameterized type, plus all types
// reachable from that type using reflection, even though
// these runtime types may not be required for them.
//
// Rather than break existing programs that rely on
// AllFunctions visiting extra methods that are unreferenced
// by IR and unreachable via reflection, we moved the logic
// here, unprincipled though it is.
// (See doc comment for better ideas.)
//
// Nonetheless, after the move, we no longer visit every
// method of any type recursively reachable from T, only the
// methods of T and *T themselves, and we only apply this to
// named types T, and not to the type of every exported
// package member.
exportedTypeHack := func(t *ssa.Type) {
if isSyntactic(t.Package()) &&
ast.IsExported(t.Name()) &&
!types.IsInterface(t.Type()) {
// Consider only named types.
// (Ignore aliases and unsafe.Pointer.)
if named, ok := t.Type().(*types.Named); ok {
if named.TypeParams() == nil {
methodsOf(named) // T
methodsOf(types.NewPointer(named)) // *T
}
}
}
}
for _, pkg := range prog.AllPackages() {
for _, mem := range pkg.Members {
switch mem := mem.(type) {
case *ssa.Function:
// Visit all package-level declared functions.
function(mem)
case *ssa.Type:
exportedTypeHack(mem)
}
}
}
// Visit all methods of types for which runtime types were
// materialized, as they are reachable through reflection.
for _, T := range prog.RuntimeTypes() {
methodsOf(T)
}
return seen
}
// MainPackages returns the subset of the specified packages
// named "main" that define a main function.
// The result may include synthetic "testmain" packages.
func MainPackages(pkgs []*ssa.Package) []*ssa.Package {
var mains []*ssa.Package
for _, pkg := range pkgs {
if pkg.Pkg.Name() == "main" && pkg.Func("main") != nil {
mains = append(mains, pkg)
}
}
return mains
}
// TODO(adonovan): propose a principled API for this. One possibility
// is a new field, Package.SrcFunctions []*Function, which would
// contain the list of SrcFunctions described in point 2 of the
// AllFunctions doc comment, or nil if the package is not from syntax.
// But perhaps overloading nil vs empty slice is too subtle.
//
//go:linkname isSyntactic golang.org/x/tools/go/ssa.isSyntactic
func isSyntactic(pkg *ssa.Package) bool

567
vendor/golang.org/x/tools/go/ssa/subst.go generated vendored Normal file
View File

@@ -0,0 +1,567 @@
// Copyright 2022 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 ssa
import (
"go/types"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/aliases"
)
// subster defines a type substitution operation of a set of type parameters
// to type parameter free replacement types. Substitution is done within
// the context of a package-level function instantiation. *Named types
// declared in the function are unique to the instantiation.
//
// For example, given a parameterized function F
//
// func F[S, T any]() any {
// type X struct{ s S; next *X }
// var p *X
// return p
// }
//
// calling the instantiation F[string, int]() returns an interface
// value (*X[string,int], nil) where the underlying value of
// X[string,int] is a struct{s string; next *X[string,int]}.
//
// A nil *subster is a valid, empty substitution map. It always acts as
// the identity function. This allows for treating parameterized and
// non-parameterized functions identically while compiling to ssa.
//
// Not concurrency-safe.
//
// Note: Some may find it helpful to think through some of the most
// complex substitution cases using lambda calculus inspired notation.
// subst.typ() solves evaluating a type expression E
// within the body of a function Fn[m] with the type parameters m
// once we have applied the type arguments N.
// We can succinctly write this as a function application:
//
// ((λm. E) N)
//
// go/types does not provide this interface directly.
// So what subster provides is a type substitution operation
//
// E[m:=N]
type subster struct {
replacements map[*types.TypeParam]types.Type // values should contain no type params
cache map[types.Type]types.Type // cache of subst results
origin *types.Func // types.Objects declared within this origin function are unique within this context
ctxt *types.Context // speeds up repeated instantiations
uniqueness typeutil.Map // determines the uniqueness of the instantiations within the function
// TODO(taking): consider adding Pos
}
// Returns a subster that replaces tparams[i] with targs[i]. Uses ctxt as a cache.
// targs should not contain any types in tparams.
// fn is the generic function for which we are substituting.
func makeSubster(ctxt *types.Context, fn *types.Func, tparams *types.TypeParamList, targs []types.Type) *subster {
assert(tparams.Len() == len(targs), "makeSubster argument count must match")
subst := &subster{
replacements: make(map[*types.TypeParam]types.Type, tparams.Len()),
cache: make(map[types.Type]types.Type),
origin: fn.Origin(),
ctxt: ctxt,
}
for i := 0; i < tparams.Len(); i++ {
subst.replacements[tparams.At(i)] = targs[i]
}
return subst
}
// typ returns the type of t with the type parameter tparams[i] substituted
// for the type targs[i] where subst was created using tparams and targs.
func (subst *subster) typ(t types.Type) (res types.Type) {
if subst == nil {
return t // A nil subst is type preserving.
}
if r, ok := subst.cache[t]; ok {
return r
}
defer func() {
subst.cache[t] = res
}()
switch t := t.(type) {
case *types.TypeParam:
if r := subst.replacements[t]; r != nil {
return r
}
return t
case *types.Basic:
return t
case *types.Array:
if r := subst.typ(t.Elem()); r != t.Elem() {
return types.NewArray(r, t.Len())
}
return t
case *types.Slice:
if r := subst.typ(t.Elem()); r != t.Elem() {
return types.NewSlice(r)
}
return t
case *types.Pointer:
if r := subst.typ(t.Elem()); r != t.Elem() {
return types.NewPointer(r)
}
return t
case *types.Tuple:
return subst.tuple(t)
case *types.Struct:
return subst.struct_(t)
case *types.Map:
key := subst.typ(t.Key())
elem := subst.typ(t.Elem())
if key != t.Key() || elem != t.Elem() {
return types.NewMap(key, elem)
}
return t
case *types.Chan:
if elem := subst.typ(t.Elem()); elem != t.Elem() {
return types.NewChan(t.Dir(), elem)
}
return t
case *types.Signature:
return subst.signature(t)
case *types.Union:
return subst.union(t)
case *types.Interface:
return subst.interface_(t)
case *types.Alias:
return subst.alias(t)
case *types.Named:
return subst.named(t)
case *opaqueType:
return t // opaque types are never substituted
default:
panic("unreachable")
}
}
// types returns the result of {subst.typ(ts[i])}.
func (subst *subster) types(ts []types.Type) []types.Type {
res := make([]types.Type, len(ts))
for i := range ts {
res[i] = subst.typ(ts[i])
}
return res
}
func (subst *subster) tuple(t *types.Tuple) *types.Tuple {
if t != nil {
if vars := subst.varlist(t); vars != nil {
return types.NewTuple(vars...)
}
}
return t
}
type varlist interface {
At(i int) *types.Var
Len() int
}
// fieldlist is an adapter for structs for the varlist interface.
type fieldlist struct {
str *types.Struct
}
func (fl fieldlist) At(i int) *types.Var { return fl.str.Field(i) }
func (fl fieldlist) Len() int { return fl.str.NumFields() }
func (subst *subster) struct_(t *types.Struct) *types.Struct {
if t != nil {
if fields := subst.varlist(fieldlist{t}); fields != nil {
tags := make([]string, t.NumFields())
for i, n := 0, t.NumFields(); i < n; i++ {
tags[i] = t.Tag(i)
}
return types.NewStruct(fields, tags)
}
}
return t
}
// varlist returns subst(in[i]) or return nils if subst(v[i]) == v[i] for all i.
func (subst *subster) varlist(in varlist) []*types.Var {
var out []*types.Var // nil => no updates
for i, n := 0, in.Len(); i < n; i++ {
v := in.At(i)
w := subst.var_(v)
if v != w && out == nil {
out = make([]*types.Var, n)
for j := 0; j < i; j++ {
out[j] = in.At(j)
}
}
if out != nil {
out[i] = w
}
}
return out
}
func (subst *subster) var_(v *types.Var) *types.Var {
if v != nil {
if typ := subst.typ(v.Type()); typ != v.Type() {
if v.IsField() {
return types.NewField(v.Pos(), v.Pkg(), v.Name(), typ, v.Embedded())
}
return types.NewParam(v.Pos(), v.Pkg(), v.Name(), typ)
}
}
return v
}
func (subst *subster) union(u *types.Union) *types.Union {
var out []*types.Term // nil => no updates
for i, n := 0, u.Len(); i < n; i++ {
t := u.Term(i)
r := subst.typ(t.Type())
if r != t.Type() && out == nil {
out = make([]*types.Term, n)
for j := 0; j < i; j++ {
out[j] = u.Term(j)
}
}
if out != nil {
out[i] = types.NewTerm(t.Tilde(), r)
}
}
if out != nil {
return types.NewUnion(out)
}
return u
}
func (subst *subster) interface_(iface *types.Interface) *types.Interface {
if iface == nil {
return nil
}
// methods for the interface. Initially nil if there is no known change needed.
// Signatures for the method where recv is nil. NewInterfaceType fills in the receivers.
var methods []*types.Func
initMethods := func(n int) { // copy first n explicit methods
methods = make([]*types.Func, iface.NumExplicitMethods())
for i := range n {
f := iface.ExplicitMethod(i)
norecv := changeRecv(f.Type().(*types.Signature), nil)
methods[i] = types.NewFunc(f.Pos(), f.Pkg(), f.Name(), norecv)
}
}
for i := 0; i < iface.NumExplicitMethods(); i++ {
f := iface.ExplicitMethod(i)
// On interfaces, we need to cycle break on anonymous interface types
// being in a cycle with their signatures being in cycles with their receivers
// that do not go through a Named.
norecv := changeRecv(f.Type().(*types.Signature), nil)
sig := subst.typ(norecv)
if sig != norecv && methods == nil {
initMethods(i)
}
if methods != nil {
methods[i] = types.NewFunc(f.Pos(), f.Pkg(), f.Name(), sig.(*types.Signature))
}
}
var embeds []types.Type
initEmbeds := func(n int) { // copy first n embedded types
embeds = make([]types.Type, iface.NumEmbeddeds())
for i := range n {
embeds[i] = iface.EmbeddedType(i)
}
}
for i := 0; i < iface.NumEmbeddeds(); i++ {
e := iface.EmbeddedType(i)
r := subst.typ(e)
if e != r && embeds == nil {
initEmbeds(i)
}
if embeds != nil {
embeds[i] = r
}
}
if methods == nil && embeds == nil {
return iface
}
if methods == nil {
initMethods(iface.NumExplicitMethods())
}
if embeds == nil {
initEmbeds(iface.NumEmbeddeds())
}
return types.NewInterfaceType(methods, embeds).Complete()
}
func (subst *subster) alias(t *types.Alias) types.Type {
// See subster.named. This follows the same strategy.
tparams := aliases.TypeParams(t)
targs := aliases.TypeArgs(t)
tname := t.Obj()
torigin := aliases.Origin(t)
if !declaredWithin(tname, subst.origin) {
// t is declared outside of the function origin. So t is a package level type alias.
if targs.Len() == 0 {
// No type arguments so no instantiation needed.
return t
}
// Instantiate with the substituted type arguments.
newTArgs := subst.typelist(targs)
return subst.instantiate(torigin, newTArgs)
}
if targs.Len() == 0 {
// t is declared within the function origin and has no type arguments.
//
// Example: This corresponds to A or B in F, but not A[int]:
//
// func F[T any]() {
// type A[S any] = struct{t T, s S}
// type B = T
// var x A[int]
// ...
// }
//
// This is somewhat different than *Named as *Alias cannot be created recursively.
// Copy and substitute type params.
var newTParams []*types.TypeParam
for cur := range tparams.TypeParams() {
cobj := cur.Obj()
cname := types.NewTypeName(cobj.Pos(), cobj.Pkg(), cobj.Name(), nil)
ntp := types.NewTypeParam(cname, nil)
subst.cache[cur] = ntp // See the comment "Note: Subtle" in subster.named.
newTParams = append(newTParams, ntp)
}
// Substitute rhs.
rhs := subst.typ(aliases.Rhs(t))
// Create the fresh alias.
//
// Until 1.27, the result of aliases.NewAlias(...).Type() cannot guarantee it is a *types.Alias.
// However, as t is an *alias.Alias and t is well-typed, then aliases must have been enabled.
// Follow this decision, and always enable aliases here.
const enabled = true
obj := aliases.NewAlias(enabled, tname.Pos(), tname.Pkg(), tname.Name(), rhs, newTParams)
// Substitute into all of the constraints after they are created.
for i, ntp := range newTParams {
bound := tparams.At(i).Constraint()
ntp.SetConstraint(subst.typ(bound))
}
return obj.Type()
}
// t is declared within the function origin and has type arguments.
//
// Example: This corresponds to A[int] in F. Cases A and B are handled above.
// func F[T any]() {
// type A[S any] = struct{t T, s S}
// type B = T
// var x A[int]
// ...
// }
subOrigin := subst.typ(torigin)
subTArgs := subst.typelist(targs)
return subst.instantiate(subOrigin, subTArgs)
}
func (subst *subster) named(t *types.Named) types.Type {
// A Named type is a user defined type.
// Ignoring generics, Named types are canonical: they are identical if
// and only if they have the same defining symbol.
// Generics complicate things, both if the type definition itself is
// parameterized, and if the type is defined within the scope of a
// parameterized function. In this case, two named types are identical if
// and only if their identifying symbols are identical, and all type
// arguments bindings in scope of the named type definition (including the
// type parameters of the definition itself) are equivalent.
//
// Notably:
// 1. For type definition type T[P1 any] struct{}, T[A] and T[B] are identical
// only if A and B are identical.
// 2. Inside the generic func Fn[m any]() any { type T struct{}; return T{} },
// the result of Fn[A] and Fn[B] have identical type if and only if A and
// B are identical.
// 3. Both 1 and 2 could apply, such as in
// func F[m any]() any { type T[x any] struct{}; return T{} }
//
// A subster replaces type parameters within a function scope, and therefore must
// also replace free type parameters in the definitions of local types.
//
// Note: There are some detailed notes sprinkled throughout that borrow from
// lambda calculus notation. These contain some over simplifying math.
//
// LC: One way to think about subster is that it is a way of evaluating
// ((λm. E) N) as E[m:=N].
// Each Named type t has an object *TypeName within a scope S that binds an
// underlying type expression U. U can refer to symbols within S (+ S's ancestors).
// Let x = t.TypeParams() and A = t.TypeArgs().
// Each Named type t is then either:
// U where len(x) == 0 && len(A) == 0
// λx. U where len(x) != 0 && len(A) == 0
// ((λx. U) A) where len(x) == len(A)
// In each case, we will evaluate t[m:=N].
tparams := t.TypeParams() // x
targs := t.TypeArgs() // A
if !declaredWithin(t.Obj(), subst.origin) {
// t is declared outside of Fn[m].
//
// In this case, we can skip substituting t.Underlying().
// The underlying type cannot refer to the type parameters.
//
// LC: Let free(E) be the set of free type parameters in an expression E.
// Then whenever m ∉ free(E), then E = E[m:=N].
// t ∉ Scope(fn) so therefore m ∉ free(U) and m ∩ x = ∅.
if targs.Len() == 0 {
// t has no type arguments. So it does not need to be instantiated.
//
// This is the normal case in real Go code, where t is not parameterized,
// declared at some package scope, and m is a TypeParam from a parameterized
// function F[m] or method.
//
// LC: m ∉ free(A) lets us conclude m ∉ free(t). So t=t[m:=N].
return t
}
// t is declared outside of Fn[m] and has type arguments.
// The type arguments may contain type parameters m so
// substitute the type arguments, and instantiate the substituted
// type arguments.
//
// LC: Evaluate this as ((λx. U) A') where A' = A[m := N].
newTArgs := subst.typelist(targs)
return subst.instantiate(t.Origin(), newTArgs)
}
// t is declared within Fn[m].
if targs.Len() == 0 { // no type arguments?
assert(t == t.Origin(), "local parameterized type abstraction must be an origin type")
// t has no type arguments.
// The underlying type of t may contain the function's type parameters,
// replace these, and create a new type.
//
// Subtle: We short circuit substitution and use a newly created type in
// subst, i.e. cache[t]=fresh, to preemptively replace t with fresh
// in recursive types during traversal. This both breaks infinite cycles
// and allows for constructing types with the replacement applied in
// subst.typ(U).
//
// A new copy of the Named and Typename (and constraints) per function
// instantiation matches the semantics of Go, which treats all function
// instantiations F[N] as having distinct local types.
//
// LC: x.Len()=0 can be thought of as a special case of λx. U.
// LC: Evaluate (λx. U)[m:=N] as (λx'. U') where U'=U[x:=x',m:=N].
tname := t.Obj()
obj := types.NewTypeName(tname.Pos(), tname.Pkg(), tname.Name(), nil)
fresh := types.NewNamed(obj, nil, nil)
var newTParams []*types.TypeParam
for cur := range tparams.TypeParams() {
cobj := cur.Obj()
cname := types.NewTypeName(cobj.Pos(), cobj.Pkg(), cobj.Name(), nil)
ntp := types.NewTypeParam(cname, nil)
subst.cache[cur] = ntp
newTParams = append(newTParams, ntp)
}
fresh.SetTypeParams(newTParams)
subst.cache[t] = fresh
subst.cache[fresh] = fresh
fresh.SetUnderlying(subst.typ(t.Underlying()))
// Substitute into all of the constraints after they are created.
for i, ntp := range newTParams {
bound := tparams.At(i).Constraint()
ntp.SetConstraint(subst.typ(bound))
}
return fresh
}
// t is defined within Fn[m] and t has type arguments (an instantiation).
// We reduce this to the two cases above:
// (1) substitute the function's type parameters into t.Origin().
// (2) substitute t's type arguments A and instantiate the updated t.Origin() with these.
//
// LC: Evaluate ((λx. U) A)[m:=N] as (t' A') where t' = (λx. U)[m:=N] and A'=A [m:=N]
subOrigin := subst.typ(t.Origin())
subTArgs := subst.typelist(targs)
return subst.instantiate(subOrigin, subTArgs)
}
func (subst *subster) instantiate(orig types.Type, targs []types.Type) types.Type {
i, err := types.Instantiate(subst.ctxt, orig, targs, false)
assert(err == nil, "failed to Instantiate named (Named or Alias) type")
if c, _ := subst.uniqueness.At(i).(types.Type); c != nil {
return c.(types.Type)
}
subst.uniqueness.Set(i, i)
return i
}
func (subst *subster) typelist(l *types.TypeList) []types.Type {
res := make([]types.Type, l.Len())
for i := 0; i < l.Len(); i++ {
res[i] = subst.typ(l.At(i))
}
return res
}
func (subst *subster) signature(t *types.Signature) types.Type {
tparams := t.TypeParams()
// We are choosing not to support tparams.Len() > 0 until a need has been observed in practice.
//
// There are some known usages for types.Types coming from types.{Eval,CheckExpr}.
// To support tparams.Len() > 0, we just need to do the following [pseudocode]:
// targs := {subst.replacements[tparams[i]]]}; Instantiate(ctxt, t, targs, false)
assert(tparams.Len() == 0, "Substituting types.Signatures with generic functions are currently unsupported.")
// Either:
// (1)non-generic function.
// no type params to substitute
// (2)generic method and recv needs to be substituted.
// Receivers can be either:
// named
// pointer to named
// interface
// nil
// interface is the problematic case. We need to cycle break there!
recv := subst.var_(t.Recv())
params := subst.tuple(t.Params())
results := subst.tuple(t.Results())
if recv != t.Recv() || params != t.Params() || results != t.Results() {
return types.NewSignatureType(recv, nil, nil, params, results, t.Variadic())
}
return t
}

103
vendor/golang.org/x/tools/go/ssa/task.go generated vendored Normal file
View File

@@ -0,0 +1,103 @@
// 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.
package ssa
import (
"sync/atomic"
)
// Each task has two states: it is initially "active",
// and transitions to "done".
//
// tasks form a directed graph. An edge from x to y (with y in x.edges)
// indicates that the task x waits on the task y to be done.
// Cycles are permitted.
//
// Calling x.wait() blocks the calling goroutine until task x,
// and all the tasks transitively reachable from x are done.
//
// The nil *task is always considered done.
type task struct {
done chan unit // close when the task is done.
edges map[*task]unit // set of predecessors of this task.
transitive atomic.Bool // true once it is known all predecessors are done.
}
func (x *task) isTransitivelyDone() bool { return x == nil || x.transitive.Load() }
// addEdge creates an edge from x to y, indicating that
// x.wait() will not return before y is done.
// All calls to x.addEdge(...) should happen before x.markDone().
func (x *task) addEdge(y *task) {
if x == y || y.isTransitivelyDone() {
return // no work remaining
}
// heuristic done check
select {
case <-x.done:
panic("cannot add an edge to a done task")
default:
}
if x.edges == nil {
x.edges = make(map[*task]unit)
}
x.edges[y] = unit{}
}
// markDone changes the task's state to markDone.
func (x *task) markDone() {
if x != nil {
close(x.done)
}
}
// wait blocks until x and all the tasks it can reach through edges are done.
func (x *task) wait() {
if x.isTransitivelyDone() {
return // already known to be done. Skip allocations.
}
// Use BFS to wait on u.done to be closed, for all u transitively
// reachable from x via edges.
//
// This work can be repeated by multiple workers doing wait().
//
// Note: Tarjan's SCC algorithm is able to mark SCCs as transitively done
// as soon as the SCC has been visited. This is theoretically faster, but is
// a more complex algorithm. Until we have evidence, we need the more complex
// algorithm, the simpler algorithm BFS is implemented.
//
// In Go 1.23, ssa/TestStdlib reaches <=3 *tasks per wait() in most schedules
// On some schedules, there is a cycle building net/http and internal/trace/testtrace
// due to slices functions.
work := []*task{x}
enqueued := map[*task]unit{x: {}}
for i := 0; i < len(work); i++ {
u := work[i]
if u.isTransitivelyDone() { // already transitively done
work[i] = nil
continue
}
<-u.done // wait for u to be marked done.
for v := range u.edges {
if _, ok := enqueued[v]; !ok {
enqueued[v] = unit{}
work = append(work, v)
}
}
}
// work is transitively closed over dependencies.
// u in work is done (or transitively done and skipped).
// u is transitively done.
for _, u := range work {
if u != nil {
x.transitive.Store(true)
}
}
}

179
vendor/golang.org/x/tools/go/ssa/typeset.go generated vendored Normal file
View File

@@ -0,0 +1,179 @@
// Copyright 2022 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 ssa
import (
"go/types"
"golang.org/x/tools/internal/typeparams"
)
// Utilities for dealing with type sets.
const debug = false
// typeset is an iterator over the (type/underlying type) pairs of the
// specific type terms of the type set implied by t.
// If t is a type parameter, the implied type set is the type set of t's constraint.
// In that case, if there are no specific terms, typeset calls yield with (nil, nil).
// If t is not a type parameter, the implied type set consists of just t.
// In any case, typeset is guaranteed to call yield at least once.
func typeset(typ types.Type, yield func(t, u types.Type) bool) {
switch typ := types.Unalias(typ).(type) {
case *types.TypeParam, *types.Interface:
terms := termListOf(typ)
if len(terms) == 0 {
yield(nil, nil)
return
}
for _, term := range terms {
u := types.Unalias(term.Type())
if !term.Tilde() {
u = u.Underlying()
}
if debug {
assert(types.Identical(u, u.Underlying()), "Unalias(x) == under(x) for ~x terms")
}
if !yield(term.Type(), u) {
break
}
}
return
default:
yield(typ, typ.Underlying())
}
}
// termListOf returns the type set of typ as a normalized term set. Returns an empty set on an error.
func termListOf(typ types.Type) []*types.Term {
terms, err := typeparams.NormalTerms(typ)
if err != nil {
return nil
}
return terms
}
// typeSetIsEmpty returns true if a typeset is empty.
func typeSetIsEmpty(typ types.Type) bool {
var empty bool
typeset(typ, func(t, _ types.Type) bool {
empty = t == nil
return false
})
return empty
}
// isBytestring returns true if T has the same terms as interface{[]byte | string}.
// These act like a core type for some operations: slice expressions, append and copy.
//
// See https://go.dev/ref/spec#Core_types for the details on bytestring.
func isBytestring(T types.Type) bool {
U := T.Underlying()
if _, ok := U.(*types.Interface); !ok {
return false
}
hasBytes, hasString := false, false
ok := underIs(U, func(t types.Type) bool {
switch {
case isString(t):
hasString = true
return true
case isByteSlice(t):
hasBytes = true
return true
default:
return false
}
})
return ok && hasBytes && hasString
}
// underIs calls f with the underlying types of the type terms
// of the type set of typ and reports whether all calls to f returned true.
// If there are no specific terms, underIs returns the result of f(nil).
func underIs(typ types.Type, f func(types.Type) bool) bool {
var ok bool
typeset(typ, func(t, u types.Type) bool {
ok = f(u)
return ok
})
return ok
}
// indexType returns the element type and index mode of a IndexExpr over a type.
// It returns an invalid mode if the type is not indexable; this should never occur in a well-typed program.
func indexType(typ types.Type) (types.Type, indexMode) {
switch U := typ.Underlying().(type) {
case *types.Array:
return U.Elem(), ixArrVar
case *types.Pointer:
if arr, ok := U.Elem().Underlying().(*types.Array); ok {
return arr.Elem(), ixVar
}
case *types.Slice:
return U.Elem(), ixVar
case *types.Map:
return U.Elem(), ixMap
case *types.Basic:
return tByte, ixValue // must be a string
case *types.Interface:
var elem types.Type
mode := ixInvalid
typeset(typ, func(t, _ types.Type) bool {
if t == nil {
return false // empty set
}
e, m := indexType(t)
if elem == nil {
elem, mode = e, m
}
if debug && !types.Identical(elem, e) { // if type checked, just a sanity check
mode = ixInvalid
return false
}
// Update the mode to the most constrained address type.
mode = mode.meet(m)
return mode != ixInvalid
})
return elem, mode
}
return nil, ixInvalid
}
// An indexMode specifies the (addressing) mode of an index operand.
//
// Addressing mode of an index operation is based on the set of
// underlying types.
// Hasse diagram of the indexMode meet semi-lattice:
//
// ixVar ixMap
// | |
// ixArrVar |
// | |
// ixValue |
// \ /
// ixInvalid
type indexMode byte
const (
ixInvalid indexMode = iota // index is invalid
ixValue // index is a computed value (not addressable)
ixArrVar // like ixVar, but index operand contains an array
ixVar // index is an addressable variable
ixMap // index is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
)
// meet is the address type that is constrained by both x and y.
func (x indexMode) meet(y indexMode) indexMode {
if (x == ixMap || y == ixMap) && x != y {
return ixInvalid
}
// Use int representation and return min.
if x < y {
return y
}
return x
}

413
vendor/golang.org/x/tools/go/ssa/util.go generated vendored Normal file
View File

@@ -0,0 +1,413 @@
// Copyright 2013 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 ssa
// This file defines a number of miscellaneous utility functions.
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"io"
"os"
"sync"
_ "unsafe" // for go:linkname hack
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/typesinternal"
)
type unit struct{}
//// Sanity checking utilities
// assert panics with the message msg if p is false.
// Avoid combining with expensive string formatting.
func assert(p bool, msg string) {
if !p {
panic(msg)
}
}
//// AST utilities
// isBlankIdent returns true iff e is an Ident with name "_".
// They have no associated types.Object, and thus no type.
func isBlankIdent(e ast.Expr) bool {
id, ok := e.(*ast.Ident)
return ok && id.Name == "_"
}
//// Type utilities. Some of these belong in go/types.
// isNonTypeParamInterface reports whether t is an interface type but not a type parameter.
func isNonTypeParamInterface(t types.Type) bool {
return !typeparams.IsTypeParam(t) && types.IsInterface(t)
}
// isBasic reports whether t is a basic type.
// t is assumed to be an Underlying type (not Named or Alias).
func isBasic(t types.Type) bool {
_, ok := t.(*types.Basic)
return ok
}
// isString reports whether t is exactly a string type.
// t is assumed to be an Underlying type (not Named or Alias).
func isString(t types.Type) bool {
basic, ok := t.(*types.Basic)
return ok && basic.Info()&types.IsString != 0
}
// isByteSlice reports whether t is of the form []~bytes.
// t is assumed to be an Underlying type (not Named or Alias).
func isByteSlice(t types.Type) bool {
if b, ok := t.(*types.Slice); ok {
e, _ := b.Elem().Underlying().(*types.Basic)
return e != nil && e.Kind() == types.Byte
}
return false
}
// isRuneSlice reports whether t is of the form []~runes.
// t is assumed to be an Underlying type (not Named or Alias).
func isRuneSlice(t types.Type) bool {
if b, ok := t.(*types.Slice); ok {
e, _ := b.Elem().Underlying().(*types.Basic)
return e != nil && e.Kind() == types.Rune
}
return false
}
// isBasicConvTypes returns true when the type set of a type
// can be one side of a Convert operation. This is when:
// - All are basic, []byte, or []rune.
// - At least 1 is basic.
// - At most 1 is []byte or []rune.
func isBasicConvTypes(typ types.Type) bool {
basics, cnt := 0, 0
ok := underIs(typ, func(t types.Type) bool {
cnt++
if isBasic(t) {
basics++
return true
}
return isByteSlice(t) || isRuneSlice(t)
})
return ok && basics >= 1 && cnt-basics <= 1
}
// isPointer reports whether t's underlying type is a pointer.
func isPointer(t types.Type) bool {
return is[*types.Pointer](t.Underlying())
}
// isPointerCore reports whether t's core type is a pointer.
//
// (Most pointer manipulation is related to receivers, in which case
// isPointer is appropriate. tecallers can use isPointer(t).
func isPointerCore(t types.Type) bool {
return is[*types.Pointer](typeparams.CoreType(t))
}
func is[T any](x any) bool {
_, ok := x.(T)
return ok
}
// recvType returns the receiver type of method obj.
func recvType(obj *types.Func) types.Type {
return obj.Signature().Recv().Type()
}
// fieldOf returns the index'th field of the (core type of) a struct type;
// otherwise returns nil.
func fieldOf(typ types.Type, index int) *types.Var {
if st, ok := typeparams.CoreType(typ).(*types.Struct); ok {
if 0 <= index && index < st.NumFields() {
return st.Field(index)
}
}
return nil
}
// isUntyped reports whether typ is the type of an untyped constant.
func isUntyped(typ types.Type) bool {
// No Underlying/Unalias: untyped constant types cannot be Named or Alias.
b, ok := typ.(*types.Basic)
return ok && b.Info()&types.IsUntyped != 0
}
// declaredWithin reports whether an object is declared within a function.
//
// obj must not be a method or a field.
func declaredWithin(obj types.Object, fn *types.Func) bool {
if obj.Pos() != token.NoPos {
return fn.Scope().Contains(obj.Pos()) // trust the positions if they exist.
}
if fn.Pkg() != obj.Pkg() {
return false // fast path for different packages
}
// Traverse Parent() scopes for fn.Scope().
for p := obj.Parent(); p != nil; p = p.Parent() {
if p == fn.Scope() {
return true
}
}
return false
}
// logStack prints the formatted "start" message to stderr and
// returns a closure that prints the corresponding "end" message.
// Call using 'defer logStack(...)()' to show builder stack on panic.
// Don't forget trailing parens!
func logStack(format string, args ...any) func() {
msg := fmt.Sprintf(format, args...)
io.WriteString(os.Stderr, msg)
io.WriteString(os.Stderr, "\n")
return func() {
io.WriteString(os.Stderr, msg)
io.WriteString(os.Stderr, " end\n")
}
}
// newVar creates a 'var' for use in a types.Tuple.
func newVar(name string, typ types.Type) *types.Var {
return types.NewParam(token.NoPos, nil, name, typ)
}
// anonVar creates an anonymous 'var' for use in a types.Tuple.
func anonVar(typ types.Type) *types.Var {
return newVar("", typ)
}
var lenResults = types.NewTuple(anonVar(tInt))
// makeLen returns the len builtin specialized to type func(T)int.
func makeLen(T types.Type) *Builtin {
lenParams := types.NewTuple(anonVar(T))
return &Builtin{
name: "len",
sig: types.NewSignatureType(nil, nil, nil, lenParams, lenResults, false),
}
}
// receiverTypeArgs returns the type arguments to a method's receiver.
// Returns an empty list if the receiver does not have type arguments.
func receiverTypeArgs(method *types.Func) []types.Type {
recv := method.Signature().Recv()
_, named := typesinternal.ReceiverNamed(recv)
if named == nil {
return nil // recv is anonymous struct/interface
}
ts := named.TypeArgs()
if ts.Len() == 0 {
return nil
}
targs := make([]types.Type, ts.Len())
for i := 0; i < ts.Len(); i++ {
targs[i] = ts.At(i)
}
return targs
}
// recvAsFirstArg takes a method signature and returns a function
// signature with receiver as the first parameter.
func recvAsFirstArg(sig *types.Signature) *types.Signature {
params := make([]*types.Var, 0, 1+sig.Params().Len())
params = append(params, sig.Recv())
for v := range sig.Params().Variables() {
params = append(params, v)
}
return types.NewSignatureType(nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic())
}
// instance returns whether an expression is a simple or qualified identifier
// that is a generic instantiation.
func instance(info *types.Info, expr ast.Expr) bool {
// Compare the logic here against go/types.instantiatedIdent,
// which also handles *IndexExpr and *IndexListExpr.
var id *ast.Ident
switch x := expr.(type) {
case *ast.Ident:
id = x
case *ast.SelectorExpr:
id = x.Sel
default:
return false
}
_, ok := info.Instances[id]
return ok
}
// instanceArgs returns the Instance[id].TypeArgs as a slice.
func instanceArgs(info *types.Info, id *ast.Ident) []types.Type {
targList := info.Instances[id].TypeArgs
if targList == nil {
return nil
}
targs := make([]types.Type, targList.Len())
for i, n := 0, targList.Len(); i < n; i++ {
targs[i] = targList.At(i)
}
return targs
}
// Mapping of a type T to a canonical instance C s.t. types.Identical(T, C).
// Thread-safe.
type canonizer struct {
mu sync.Mutex
types typeutil.Map // map from type to a canonical instance
lists typeListMap // map from a list of types to a canonical instance
}
func newCanonizer() *canonizer {
c := &canonizer{}
h := typeutil.MakeHasher()
c.types.SetHasher(h)
c.lists.hasher = h
return c
}
// List returns a canonical representative of a list of types.
// Representative of the empty list is nil.
func (c *canonizer) List(ts []types.Type) *typeList {
if len(ts) == 0 {
return nil
}
unaliasAll := func(ts []types.Type) []types.Type {
// Is there some top level alias?
var found bool
for _, t := range ts {
if _, ok := t.(*types.Alias); ok {
found = true
break
}
}
if !found {
return ts // no top level alias
}
cp := make([]types.Type, len(ts)) // copy with top level aliases removed.
for i, t := range ts {
cp[i] = types.Unalias(t)
}
return cp
}
l := unaliasAll(ts)
c.mu.Lock()
defer c.mu.Unlock()
return c.lists.rep(l)
}
// Type returns a canonical representative of type T.
// Removes top-level aliases.
//
// For performance, reasons the canonical instance is order-dependent,
// and may contain deeply nested aliases.
func (c *canonizer) Type(T types.Type) types.Type {
T = types.Unalias(T) // remove the top level alias.
c.mu.Lock()
defer c.mu.Unlock()
if r := c.types.At(T); r != nil {
return r.(types.Type)
}
c.types.Set(T, T)
return T
}
// A type for representing a canonized list of types.
type typeList []types.Type
func (l *typeList) identical(ts []types.Type) bool {
if l == nil {
return len(ts) == 0
}
n := len(*l)
if len(ts) != n {
return false
}
for i, left := range *l {
right := ts[i]
if !types.Identical(left, right) {
return false
}
}
return true
}
type typeListMap struct {
hasher typeutil.Hasher
buckets map[uint32][]*typeList
}
// rep returns a canonical representative of a slice of types.
func (m *typeListMap) rep(ts []types.Type) *typeList {
if m == nil || len(ts) == 0 {
return nil
}
if m.buckets == nil {
m.buckets = make(map[uint32][]*typeList)
}
h := m.hash(ts)
bucket := m.buckets[h]
for _, l := range bucket {
if l.identical(ts) {
return l
}
}
// not present. create a representative.
cp := make(typeList, len(ts))
copy(cp, ts)
rep := &cp
m.buckets[h] = append(bucket, rep)
return rep
}
func (m *typeListMap) hash(ts []types.Type) uint32 {
if m == nil {
return 0
}
// Some smallish prime far away from typeutil.Hash.
n := len(ts)
h := uint32(13619) + 2*uint32(n)
for i := range n {
h += 3 * m.hasher.Hash(ts[i])
}
return h
}
// instantiateMethod instantiates m with targs and returns a canonical representative for this method.
func (canon *canonizer) instantiateMethod(m *types.Func, targs []types.Type, ctxt *types.Context) *types.Func {
recv := recvType(m)
if p, ok := types.Unalias(recv).(*types.Pointer); ok {
recv = p.Elem()
}
named := types.Unalias(recv).(*types.Named)
inst, err := types.Instantiate(ctxt, named.Origin(), targs, false)
if err != nil {
panic(err)
}
rep := canon.Type(inst)
obj, _, _ := types.LookupFieldOrMethod(rep, true, m.Pkg(), m.Name())
return obj.(*types.Func)
}
// Exposed to ssautil using the linkname hack.
//
//go:linkname isSyntactic golang.org/x/tools/go/ssa.isSyntactic
func isSyntactic(pkg *Package) bool { return pkg.syntax }

346
vendor/golang.org/x/tools/go/ssa/wrappers.go generated vendored Normal file
View File

@@ -0,0 +1,346 @@
// Copyright 2013 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 ssa
// This file defines synthesis of Functions that delegate to declared
// methods; they come in three kinds:
//
// (1) wrappers: methods that wrap declared methods, performing
// implicit pointer indirections and embedded field selections.
//
// (2) thunks: funcs that wrap declared methods. Like wrappers,
// thunks perform indirections and field selections. The thunk's
// first parameter is used as the receiver for the method call.
//
// (3) bounds: funcs that wrap declared methods. The bound's sole
// free variable, supplied by a closure, is used as the receiver
// for the method call. No indirections or field selections are
// performed since they can be done before the call.
import (
"fmt"
"go/token"
"go/types"
"golang.org/x/tools/internal/typeparams"
)
// -- wrappers -----------------------------------------------------------
// createWrapper returns a synthetic method that delegates to the
// declared method denoted by meth.Obj(), first performing any
// necessary pointer indirections or field selections implied by meth.
//
// The resulting method's receiver type is meth.Recv().
//
// This function is versatile but quite subtle! Consider the
// following axes of variation when making changes:
// - optional receiver indirection
// - optional implicit field selections
// - meth.Obj() may denote a concrete or an interface method
// - the result may be a thunk or a wrapper.
func createWrapper(prog *Program, sel *selection) *Function {
obj := sel.obj.(*types.Func) // the declared function
sig := sel.typ.(*types.Signature) // type of this wrapper
var recv *types.Var // wrapper's receiver or thunk's params[0]
name := obj.Name()
var description string
if sel.kind == types.MethodExpr {
name += "$thunk"
description = "thunk"
recv = sig.Params().At(0)
} else {
description = "wrapper"
recv = sig.Recv()
}
description = fmt.Sprintf("%s for %s", description, sel.obj)
if prog.mode&LogSource != 0 {
defer logStack("create %s to (%s)", description, recv.Type())()
}
/* method wrapper */
return &Function{
name: name,
method: sel,
object: obj,
Signature: sig,
Synthetic: description,
Prog: prog,
pos: obj.Pos(),
// wrappers have no syntax
build: (*builder).buildWrapper,
syntax: nil,
info: nil,
goversion: "",
}
}
// buildWrapper builds fn.Body for a method wrapper.
func (b *builder) buildWrapper(fn *Function) {
var recv *types.Var // wrapper's receiver or thunk's params[0]
var start int // first regular param
if fn.method.kind == types.MethodExpr {
recv = fn.Signature.Params().At(0)
start = 1
} else {
recv = fn.Signature.Recv()
}
fn.startBody()
fn.addSpilledParam(recv)
createParams(fn, start)
indices := fn.method.index
var v Value = fn.Locals[0] // spilled receiver
if isPointer(fn.method.recv) {
v = emitLoad(fn, v)
// For simple indirection wrappers, perform an informative nil-check:
// "value method (T).f called using nil *T pointer"
if len(indices) == 1 && !isPointer(recvType(fn.object)) {
var c Call
c.Call.Value = &Builtin{
name: "ssa:wrapnilchk",
sig: types.NewSignatureType(nil, nil, nil, types.NewTuple(anonVar(fn.method.recv), anonVar(tString), anonVar(tString)), types.NewTuple(anonVar(fn.method.recv)), false),
}
c.Call.Args = []Value{
v,
stringConst(typeparams.MustDeref(fn.method.recv).String()),
stringConst(fn.method.obj.Name()),
}
c.setType(v.Type())
v = fn.emit(&c)
}
}
// Invariant: v is a pointer, either
// value of *A receiver param, or
// address of A spilled receiver.
// We use pointer arithmetic (FieldAddr possibly followed by
// Load) in preference to value extraction (Field possibly
// preceded by Load).
v = emitImplicitSelections(fn, v, indices[:len(indices)-1], token.NoPos)
// Invariant: v is a pointer, either
// value of implicit *C field, or
// address of implicit C field.
var c Call
if r := recvType(fn.object); !types.IsInterface(r) { // concrete method
if !isPointer(r) {
v = emitLoad(fn, v)
}
c.Call.Value = fn.Prog.objectMethod(fn.object, b)
c.Call.Args = append(c.Call.Args, v)
} else {
c.Call.Method = fn.object
c.Call.Value = emitLoad(fn, v) // interface (possibly a typeparam)
}
for _, arg := range fn.Params[1:] {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c)
fn.finishBody()
}
// createParams creates parameters for wrapper method fn based on its
// Signature.Params, which do not include the receiver.
// start is the index of the first regular parameter to use.
func createParams(fn *Function, start int) {
tparams := fn.Signature.Params()
for i, n := start, tparams.Len(); i < n; i++ {
fn.addParamVar(tparams.At(i))
}
}
// -- bounds -----------------------------------------------------------
// createBound returns a bound method wrapper (or "bound"), a synthetic
// function that delegates to a concrete or interface method denoted
// by obj. The resulting function has no receiver, but has one free
// variable which will be used as the method's receiver in the
// tail-call.
//
// Use MakeClosure with such a wrapper to construct a bound method
// closure. e.g.:
//
// type T int or: type T interface { meth() }
// func (t T) meth()
// var t T
// f := t.meth
// f() // calls t.meth()
//
// f is a closure of a synthetic wrapper defined as if by:
//
// f := func() { return t.meth() }
//
// Unlike createWrapper, createBound need perform no indirection or field
// selections because that can be done before the closure is
// constructed.
func createBound(prog *Program, obj *types.Func) *Function {
description := fmt.Sprintf("bound method wrapper for %s", obj)
if prog.mode&LogSource != 0 {
defer logStack("%s", description)()
}
/* bound method wrapper */
fn := &Function{
name: obj.Name() + "$bound",
object: obj,
Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
Synthetic: description,
Prog: prog,
pos: obj.Pos(),
// wrappers have no syntax
build: (*builder).buildBound,
syntax: nil,
info: nil,
goversion: "",
}
fn.FreeVars = []*FreeVar{{name: "recv", typ: recvType(obj), parent: fn}} // (cyclic)
return fn
}
// buildBound builds fn.Body for a bound method closure.
func (b *builder) buildBound(fn *Function) {
fn.startBody()
createParams(fn, 0)
var c Call
recv := fn.FreeVars[0]
if !types.IsInterface(recvType(fn.object)) { // concrete
c.Call.Value = fn.Prog.objectMethod(fn.object, b)
c.Call.Args = []Value{recv}
} else {
c.Call.Method = fn.object
c.Call.Value = recv // interface (possibly a typeparam)
}
for _, arg := range fn.Params {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c)
fn.finishBody()
}
// -- thunks -----------------------------------------------------------
// createThunk returns a thunk, a synthetic function that delegates to a
// concrete or interface method denoted by sel.obj. The resulting
// function has no receiver, but has an additional (first) regular
// parameter.
//
// Precondition: sel.kind == types.MethodExpr.
//
// type T int or: type T interface { meth() }
// func (t T) meth()
// f := T.meth
// var t T
// f(t) // calls t.meth()
//
// f is a synthetic wrapper defined as if by:
//
// f := func(t T) { return t.meth() }
func createThunk(prog *Program, sel *selection) *Function {
if sel.kind != types.MethodExpr {
panic(sel)
}
fn := createWrapper(prog, sel)
if fn.Signature.Recv() != nil {
panic(fn) // unexpected receiver
}
return fn
}
func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
return types.NewSignatureType(recv, nil, nil, s.Params(), s.Results(), s.Variadic())
}
// A local version of *types.Selection.
// Needed for some additional control, such as creating a MethodExpr for an instantiation.
type selection struct {
kind types.SelectionKind
recv types.Type
typ types.Type
obj types.Object
index []int
indirect bool
}
func toSelection(sel *types.Selection) *selection {
return &selection{
kind: sel.Kind(),
recv: sel.Recv(),
typ: sel.Type(),
obj: sel.Obj(),
index: sel.Index(),
indirect: sel.Indirect(),
}
}
// -- instantiations --------------------------------------------------
// buildInstantiationWrapper builds the body of an instantiation
// wrapper fn. The body calls the original generic function,
// bracketed by ChangeType conversions on its arguments and results.
func (b *builder) buildInstantiationWrapper(fn *Function) {
orig := fn.topLevelOrigin
sig := fn.Signature
fn.startBody()
if sig.Recv() != nil {
fn.addParamVar(sig.Recv())
}
createParams(fn, 0)
// Create body. Add a call to origin generic function
// and make type changes between argument and parameters,
// as well as return values.
var c Call
c.Call.Value = orig
if res := orig.Signature.Results(); res.Len() == 1 {
c.typ = res.At(0).Type()
} else {
c.typ = res
}
// parameter of instance becomes an argument to the call
// to the original generic function.
argOffset := 0
for i, arg := range fn.Params {
var typ types.Type
if i == 0 && sig.Recv() != nil {
typ = orig.Signature.Recv().Type()
argOffset = 1
} else {
typ = orig.Signature.Params().At(i - argOffset).Type()
}
c.Call.Args = append(c.Call.Args, emitTypeCoercion(fn, arg, typ))
}
results := fn.emit(&c)
var ret Return
switch res := sig.Results(); res.Len() {
case 0:
// no results, do nothing.
case 1:
ret.Results = []Value{emitTypeCoercion(fn, results, res.At(0).Type())}
default:
for i := 0; i < sig.Results().Len(); i++ {
v := emitExtract(fn, results, i)
ret.Results = append(ret.Results, emitTypeCoercion(fn, v, res.At(i).Type()))
}
}
fn.emit(&ret)
fn.currentBlock = nil
fn.finishBody()
}

View File

@@ -0,0 +1,820 @@
// Copyright 2018 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 objectpath defines a naming scheme for types.Objects
// (that is, named entities in Go programs) relative to their enclosing
// package.
//
// Type-checker objects are canonical, so they are usually identified by
// their address in memory (a pointer), but a pointer has meaning only
// within one address space. By contrast, objectpath names allow the
// identity of an object to be sent from one program to another,
// establishing a correspondence between types.Object variables that are
// distinct but logically equivalent.
//
// A single object may have multiple paths. In this example,
//
// type A struct{ X int }
// type B A
//
// the field X has two paths due to its membership of both A and B.
// The For(obj) function always returns one of these paths, arbitrarily
// but consistently.
package objectpath
import (
"fmt"
"go/types"
"strconv"
"strings"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typesinternal"
)
// TODO(adonovan): think about generic aliases.
// A Path is an opaque name that identifies a types.Object
// relative to its package. Conceptually, the name consists of a
// sequence of destructuring operations applied to the package scope
// to obtain the original object.
// The name does not include the package itself.
type Path string
// Encoding
//
// An object path is a textual and (with training) human-readable encoding
// of a sequence of destructuring operators, starting from a types.Package.
// The sequences represent a path through the package/object/type graph.
// We classify these operators by their type:
//
// PO package->object Package.Scope.Lookup
// OT object->type Object.Type
// TT type->type Type.{Elem,Key,{,{,Recv}Type}Params,Results,Underlying,Rhs} [EKPRUTrCa]
// TO type->object Type.{At,Field,Method,Obj} [AFMO]
//
// All valid paths start with a package and end at an object
// and thus may be defined by the regular language:
//
// objectpath = PO (OT TT* TO)*
//
// The concrete encoding follows directly:
// - The only PO operator is Package.Scope.Lookup, which requires an identifier.
// - The only OT operator is Object.Type,
// which we encode as '.' because dot cannot appear in an identifier.
// - The TT operators are encoded as [EKPRUTrCa];
// two of these ({,Recv}TypeParams) require an integer operand,
// which is encoded as a string of decimal digits.
// - The TO operators are encoded as [AFMO];
// three of these (At,Field,Method) require an integer operand,
// which is encoded as a string of decimal digits.
// These indices are stable across different representations
// of the same package, even source and export data.
// The indices used are implementation specific and may not correspond to
// the argument to the go/types function.
//
// In the example below,
//
// package p
//
// type T interface {
// f() (a string, b struct{ X int })
// }
//
// field X has the path "T.UM0.RA1.F0",
// representing the following sequence of operations:
//
// p.Lookup("T") T
// .Type().Underlying().Method(0). f
// .Type().Results().At(1) b
// .Type().Field(0) X
//
// The encoding is not maximally compact---every R or P is
// followed by an A, for example---but this simplifies the
// encoder and decoder.
const (
// object->type operators
opType = '.' // .Type() (Object)
// type->type operators
opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map)
opKey = 'K' // .Key() (Map)
opParams = 'P' // .Params() (Signature)
opResults = 'R' // .Results() (Signature)
opUnderlying = 'U' // .Underlying() (Named)
opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature)
opRecvTypeParam = 'r' // .RecvTypeParams.At(i) (Signature)
opConstraint = 'C' // .Constraint() (TypeParam)
opRhs = 'a' // .Rhs() (Alias)
// type->object operators
opAt = 'A' // .At(i) (Tuple)
opField = 'F' // .Field(i) (Struct)
opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored)
opObj = 'O' // .Obj() (Named, TypeParam)
)
// For is equivalent to new(Encoder).For(obj).
//
// It may be more efficient to reuse a single Encoder across several calls.
func For(obj types.Object) (Path, error) {
return new(Encoder).For(obj)
}
// An Encoder amortizes the cost of encoding the paths of multiple objects.
// The zero value of an Encoder is ready to use.
type Encoder struct {
scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects
}
// For returns the path to an object relative to its package,
// or an error if the object is not accessible from the package's Scope.
//
// The For function guarantees to return a path only for the following objects:
// - package-level types
// - exported package-level non-types
// - methods
// - parameter and result variables
// - struct fields
// These objects are sufficient to define the API of their package.
// The objects described by a package's export data are drawn from this set.
//
// The set of objects accessible from a package's Scope depends on
// whether the package was produced by type-checking syntax, or
// reading export data; the latter may have a smaller Scope since
// export data trims objects that are not reachable from an exported
// declaration. For example, the For function will return a path for
// an exported method of an unexported type that is not reachable
// from any public declaration; this path will cause the Object
// function to fail if called on a package loaded from export data.
// TODO(adonovan): is this a bug or feature? Should this package
// compute accessibility in the same way?
//
// For does not return a path for predeclared names, imported package
// names, local names, and unexported package-level names (except
// types).
//
// Example: given this definition,
//
// package p
//
// type T interface {
// f() (a string, b struct{ X int })
// }
//
// For(X) would return a path that denotes the following sequence of operations:
//
// p.Scope().Lookup("T") (TypeName T)
// .Type().Underlying().Method(0). (method Func f)
// .Type().Results().At(1) (field Var b)
// .Type().Field(0) (field Var X)
//
// where p is the package (*types.Package) to which X belongs.
func (enc *Encoder) For(obj types.Object) (Path, error) {
pkg := obj.Pkg()
// This table lists the cases of interest.
//
// Object Action
// ------ ------
// nil reject
// builtin reject
// pkgname reject
// label reject
// var
// package-level accept
// func param/result accept
// local reject
// struct field accept
// const
// package-level accept
// local reject
// func
// package-level accept
// init functions reject
// concrete method accept
// interface method accept
// type
// package-level accept
// local reject
//
// The only accessible package-level objects are members of pkg itself.
//
// The cases are handled in four steps:
//
// 1. reject nil and builtin
// 2. accept package-level objects
// 3. reject obviously invalid objects
// 4. search the API for the path to the param/result/field/method.
// 1. reference to nil or builtin?
if pkg == nil {
return "", fmt.Errorf("predeclared %s has no path", obj)
}
scope := pkg.Scope()
// 2. package-level object?
if scope.Lookup(obj.Name()) == obj {
// Only exported objects (and non-exported types) have a path.
// Non-exported types may be referenced by other objects.
if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() {
return "", fmt.Errorf("no path for non-exported %v", obj)
}
return Path(obj.Name()), nil
}
// 3. Not a package-level object.
// Reject obviously non-viable cases.
switch obj := obj.(type) {
case *types.TypeName:
if _, ok := types.Unalias(obj.Type()).(*types.TypeParam); !ok {
// With the exception of type parameters, only package-level type names
// have a path.
return "", fmt.Errorf("no path for %v", obj)
}
case *types.Const, // Only package-level constants have a path.
*types.Label, // Labels are function-local.
*types.PkgName: // PkgNames are file-local.
return "", fmt.Errorf("no path for %v", obj)
case *types.Var:
// Could be:
// - a field (obj.IsField())
// - a func parameter or result
// - a local var.
// Sadly there is no way to distinguish
// a param/result from a local
// so we must proceed to the find.
case *types.Func:
// A func, if not package-level, must be a method.
if recv := obj.Signature().Recv(); recv == nil {
return "", fmt.Errorf("func is not a method: %v", obj)
}
if path, ok := enc.concreteMethod(obj); ok {
// Fast path for concrete methods that avoids looping over scope.
return path, nil
}
default:
panic(obj)
}
// 4. Search the API for the path to the var (field/param/result) or method.
// First inspect package-level named types.
// In the presence of path aliases, these give
// the best paths because non-types may
// refer to types, but not the reverse.
empty := make([]byte, 0, 48) // initial space
objs := enc.scopeObjects(scope)
for _, o := range objs {
tname, ok := o.(*types.TypeName)
if !ok {
continue // handle non-types in second pass
}
path := append(empty, o.Name()...)
path = append(path, opType)
T := o.Type()
if alias, ok := T.(*types.Alias); ok {
if r := findTypeParam(obj, aliases.TypeParams(alias), path, opTypeParam); r != nil {
return Path(r), nil
}
if r := find(obj, aliases.Rhs(alias), append(path, opRhs)); r != nil {
return Path(r), nil
}
} else if tname.IsAlias() {
// legacy alias
if r := find(obj, T, path); r != nil {
return Path(r), nil
}
} else if named, ok := T.(*types.Named); ok {
// defined (named) type
if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam); r != nil {
return Path(r), nil
}
if r := find(obj, named.Underlying(), append(path, opUnderlying)); r != nil {
return Path(r), nil
}
}
}
// Then inspect everything else:
// non-types, and declared methods of defined types.
for _, o := range objs {
path := append(empty, o.Name()...)
if _, ok := o.(*types.TypeName); !ok {
if o.Exported() {
// exported non-type (const, var, func)
if r := find(obj, o.Type(), append(path, opType)); r != nil {
return Path(r), nil
}
}
continue
}
// Inspect declared methods of defined types.
if T, ok := types.Unalias(o.Type()).(*types.Named); ok {
path = append(path, opType)
// The method index here is always with respect
// to the underlying go/types data structures,
// which ultimately derives from source order
// and must be preserved by export data.
for i := 0; i < T.NumMethods(); i++ {
m := T.Method(i)
path2 := appendOpArg(path, opMethod, i)
if m == obj {
return Path(path2), nil // found declared method
}
if r := find(obj, m.Type(), append(path2, opType)); r != nil {
return Path(r), nil
}
}
}
}
return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path())
}
func appendOpArg(path []byte, op byte, arg int) []byte {
path = append(path, op)
path = strconv.AppendInt(path, int64(arg), 10)
return path
}
// concreteMethod returns the path for meth, which must have a non-nil receiver.
// The second return value indicates success and may be false if the method is
// an interface method or if it is an instantiated method.
//
// This function is just an optimization that avoids the general scope walking
// approach. You are expected to fall back to the general approach if this
// function fails.
func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
// Concrete methods can only be declared on package-scoped named types. For
// that reason we can skip the expensive walk over the package scope: the
// path will always be package -> named type -> method. We can trivially get
// the type name from the receiver, and only have to look over the type's
// methods to find the method index.
//
// Methods on generic types require special consideration, however. Consider
// the following package:
//
// L1: type S[T any] struct{}
// L2: func (recv S[A]) Foo() { recv.Bar() }
// L3: func (recv S[B]) Bar() { }
// L4: type Alias = S[int]
// L5: func _[T any]() { var s S[int]; s.Foo() }
//
// The receivers of methods on generic types are instantiations. L2 and L3
// instantiate S with the type-parameters A and B, which are scoped to the
// respective methods. L4 and L5 each instantiate S with int. Each of these
// instantiations has its own method set, full of methods (and thus objects)
// with receivers whose types are the respective instantiations. In other
// words, we have
//
// S[A].Foo, S[A].Bar
// S[B].Foo, S[B].Bar
// S[int].Foo, S[int].Bar
//
// We may thus be trying to produce object paths for any of these objects.
//
// S[A].Foo and S[B].Bar are the origin methods, and their paths are S.Foo
// and S.Bar, which are the paths that this function naturally produces.
//
// S[A].Bar, S[B].Foo, and both methods on S[int] are instantiations that
// don't correspond to the origin methods. For S[int], this is significant.
// The most precise object path for S[int].Foo, for example, is Alias.Foo,
// not S.Foo. Our function, however, would produce S.Foo, which would
// resolve to a different object.
//
// For S[A].Bar and S[B].Foo it could be argued that S.Bar and S.Foo are
// still the correct paths, since only the origin methods have meaningful
// paths. But this is likely only true for trivial cases and has edge cases.
// Since this function is only an optimization, we err on the side of giving
// up, deferring to the slower but definitely correct algorithm. Most users
// of objectpath will only be giving us origin methods, anyway, as referring
// to instantiated methods is usually not useful.
if meth.Origin() != meth {
return "", false
}
_, named := typesinternal.ReceiverNamed(meth.Signature().Recv())
if named == nil {
return "", false
}
if types.IsInterface(named) {
// Named interfaces don't have to be package-scoped
//
// TODO(dominikh): opt: if scope.Lookup(name) == named, then we can apply this optimization to interface
// methods, too, I think.
return "", false
}
// Preallocate space for the name, opType, opMethod, and some digits.
name := named.Obj().Name()
path := make([]byte, 0, len(name)+8)
path = append(path, name...)
path = append(path, opType)
// Method indices are w.r.t. the go/types data structures,
// ultimately deriving from source order,
// which is preserved by export data.
for i := 0; i < named.NumMethods(); i++ {
if named.Method(i) == meth {
path = appendOpArg(path, opMethod, i)
return Path(path), true
}
}
// Due to golang/go#59944, go/types fails to associate the receiver with
// certain methods on cgo types.
//
// TODO(rfindley): replace this panic once golang/go#59944 is fixed in all Go
// versions gopls supports.
return "", false
// panic(fmt.Sprintf("couldn't find method %s on type %s; methods: %#v", meth, named, enc.namedMethods(named)))
}
// find finds obj within type T, returning the path to it, or nil if not found.
//
// The seen map is used to short circuit cycles through type parameters. If
// nil, it will be allocated as necessary.
//
// The seenMethods map is used internally to short circuit cycles through
// interface methods, such as occur in the following example:
//
// type I interface { f() interface{I} }
//
// See golang/go#68046 for details.
func find(obj types.Object, T types.Type, path []byte) []byte {
return (&finder{obj: obj}).find(T, path)
}
// finder closes over search state for a call to find.
type finder struct {
obj types.Object // the sought object
seenTParamNames map[*types.TypeName]bool // for cycle breaking through type parameters
seenMethods map[*types.Func]bool // for cycle breaking through recursive interfaces
}
func (f *finder) find(T types.Type, path []byte) []byte {
switch T := T.(type) {
case *types.Alias:
return f.find(types.Unalias(T), path)
case *types.Basic, *types.Named:
// Named types belonging to pkg were handled already,
// so T must belong to another package. No path.
return nil
case *types.Pointer:
return f.find(T.Elem(), append(path, opElem))
case *types.Slice:
return f.find(T.Elem(), append(path, opElem))
case *types.Array:
return f.find(T.Elem(), append(path, opElem))
case *types.Chan:
return f.find(T.Elem(), append(path, opElem))
case *types.Map:
if r := f.find(T.Key(), append(path, opKey)); r != nil {
return r
}
return f.find(T.Elem(), append(path, opElem))
case *types.Signature:
if r := f.findTypeParam(T.RecvTypeParams(), path, opRecvTypeParam); r != nil {
return r
}
if r := f.findTypeParam(T.TypeParams(), path, opTypeParam); r != nil {
return r
}
if r := f.find(T.Params(), append(path, opParams)); r != nil {
return r
}
return f.find(T.Results(), append(path, opResults))
case *types.Struct:
for i := 0; i < T.NumFields(); i++ {
fld := T.Field(i)
path2 := appendOpArg(path, opField, i)
if fld == f.obj {
return path2 // found field var
}
if r := f.find(fld.Type(), append(path2, opType)); r != nil {
return r
}
}
return nil
case *types.Tuple:
for i := 0; i < T.Len(); i++ {
v := T.At(i)
path2 := appendOpArg(path, opAt, i)
if v == f.obj {
return path2 // found param/result var
}
if r := f.find(v.Type(), append(path2, opType)); r != nil {
return r
}
}
return nil
case *types.Interface:
for i := 0; i < T.NumMethods(); i++ {
m := T.Method(i)
if f.seenMethods[m] {
return nil
}
path2 := appendOpArg(path, opMethod, i)
if m == f.obj {
return path2 // found interface method
}
if f.seenMethods == nil {
f.seenMethods = make(map[*types.Func]bool)
}
f.seenMethods[m] = true
if r := f.find(m.Type(), append(path2, opType)); r != nil {
return r
}
}
return nil
case *types.TypeParam:
name := T.Obj()
if f.seenTParamNames[name] {
return nil
}
if name == f.obj {
return append(path, opObj)
}
if f.seenTParamNames == nil {
f.seenTParamNames = make(map[*types.TypeName]bool)
}
f.seenTParamNames[name] = true
if r := f.find(T.Constraint(), append(path, opConstraint)); r != nil {
return r
}
return nil
}
panic(T)
}
func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte) []byte {
return (&finder{obj: obj}).findTypeParam(list, path, op)
}
func (f *finder) findTypeParam(list *types.TypeParamList, path []byte, op byte) []byte {
for i := 0; i < list.Len(); i++ {
tparam := list.At(i)
path2 := appendOpArg(path, op, i)
if r := f.find(tparam, path2); r != nil {
return r
}
}
return nil
}
// Object returns the object denoted by path p within the package pkg.
func Object(pkg *types.Package, p Path) (types.Object, error) {
pathstr := string(p)
if pathstr == "" {
return nil, fmt.Errorf("empty path")
}
var pkgobj, suffix string
if dot := strings.IndexByte(pathstr, opType); dot < 0 {
pkgobj = pathstr
} else {
pkgobj = pathstr[:dot]
suffix = pathstr[dot:] // suffix starts with "."
}
obj := pkg.Scope().Lookup(pkgobj)
if obj == nil {
return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj)
}
// abstraction of *types.{Pointer,Slice,Array,Chan,Map}
type hasElem interface {
Elem() types.Type
}
// abstraction of *types.{Named,Signature}
type hasTypeParams interface {
TypeParams() *types.TypeParamList
}
// abstraction of *types.{Alias,Named,TypeParam}
type hasObj interface {
Obj() *types.TypeName
}
// The loop state is the pair (t, obj),
// exactly one of which is non-nil, initially obj.
// All suffixes start with '.' (the only object->type operation),
// followed by optional type->type operations,
// then a type->object operation.
// The cycle then repeats.
var t types.Type
for suffix != "" {
code := suffix[0]
suffix = suffix[1:]
// Codes [AFMTr] have an integer operand.
var index int
switch code {
case opAt, opField, opMethod, opTypeParam, opRecvTypeParam:
rest := strings.TrimLeft(suffix, "0123456789")
numerals := suffix[:len(suffix)-len(rest)]
suffix = rest
i, err := strconv.Atoi(numerals)
if err != nil {
return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code)
}
index = int(i)
case opObj:
// no operand
default:
// The suffix must end with a type->object operation.
if suffix == "" {
return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code)
}
}
if code == opType {
if t != nil {
return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType)
}
t = obj.Type()
obj = nil
continue
}
if t == nil {
return nil, fmt.Errorf("invalid path: code %q in object context", code)
}
// Inv: t != nil, obj == nil
t = types.Unalias(t)
switch code {
case opElem:
hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t)
}
t = hasElem.Elem()
case opKey:
mapType, ok := t.(*types.Map)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t)
}
t = mapType.Key()
case opParams:
sig, ok := t.(*types.Signature)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
}
t = sig.Params()
case opResults:
sig, ok := t.(*types.Signature)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
}
t = sig.Results()
case opUnderlying:
named, ok := t.(*types.Named)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t)
}
t = named.Underlying()
case opRhs:
if alias, ok := t.(*types.Alias); ok {
t = aliases.Rhs(alias)
} else if false && aliases.Enabled() {
// The Enabled check is too expensive, so for now we
// simply assume that aliases are not enabled.
//
// Now that go1.24 is assured, we should be able to
// replace this with "if true {", but it causes tests
// to fail. TODO(adonovan): investigate.
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want alias)", code, t, t)
}
case opTypeParam:
hasTypeParams, ok := t.(hasTypeParams) // Named, Signature
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t)
}
tparams := hasTypeParams.TypeParams()
if n := tparams.Len(); index >= n {
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
}
t = tparams.At(index)
case opRecvTypeParam:
sig, ok := t.(*types.Signature) // Signature
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
}
rtparams := sig.RecvTypeParams()
if n := rtparams.Len(); index >= n {
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
}
t = rtparams.At(index)
case opConstraint:
tparam, ok := t.(*types.TypeParam)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t)
}
t = tparam.Constraint()
case opAt:
tuple, ok := t.(*types.Tuple)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want tuple)", code, t, t)
}
if n := tuple.Len(); index >= n {
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
}
obj = tuple.At(index)
t = nil
case opField:
structType, ok := t.(*types.Struct)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t)
}
if n := structType.NumFields(); index >= n {
return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n)
}
obj = structType.Field(index)
t = nil
case opMethod:
switch t := t.(type) {
case *types.Interface:
if index >= t.NumMethods() {
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
}
obj = t.Method(index) // Id-ordered
case *types.Named:
if index >= t.NumMethods() {
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
}
obj = t.Method(index)
default:
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t)
}
t = nil
case opObj:
hasObj, ok := t.(hasObj)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t)
}
obj = hasObj.Obj()
t = nil
default:
return nil, fmt.Errorf("invalid path: unknown code %q", code)
}
}
if obj == nil {
panic(p) // path does not end in an object-valued operator
}
if obj.Pkg() != pkg {
return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj)
}
return obj, nil // success
}
// scopeObjects is a memoization of scope objects.
// Callers must not modify the result.
func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object {
m := enc.scopeMemo
if m == nil {
m = make(map[*types.Scope][]types.Object)
enc.scopeMemo = m
}
objs, ok := m[scope]
if !ok {
names := scope.Names() // allocates and sorts
objs = make([]types.Object, len(names))
for i, name := range names {
objs[i] = scope.Lookup(name)
}
m[scope] = objs
}
return objs
}

86
vendor/golang.org/x/tools/go/types/typeutil/callee.go generated vendored Normal file
View File

@@ -0,0 +1,86 @@
// Copyright 2018 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 typeutil
import (
"go/ast"
"go/types"
_ "unsafe" // for linkname
)
// Callee returns the named target of a function call, if any:
// a function, method, builtin, or variable.
// It returns nil for a T(x) conversion.
//
// Functions and methods may potentially have type parameters.
//
// Note: for calls of instantiated functions and methods, Callee returns
// the corresponding generic function or method on the generic type.
func Callee(info *types.Info, call *ast.CallExpr) types.Object {
obj := info.Uses[usedIdent(info, call.Fun)]
if obj == nil {
return nil
}
if _, ok := obj.(*types.TypeName); ok {
return nil
}
return obj
}
// StaticCallee returns the target (function or method) of a static function
// call, if any. It returns nil for calls to builtins.
//
// Note: for calls of instantiated functions and methods, StaticCallee returns
// the corresponding generic function or method on the generic type.
func StaticCallee(info *types.Info, call *ast.CallExpr) *types.Func {
obj := info.Uses[usedIdent(info, call.Fun)]
fn, _ := obj.(*types.Func)
if fn == nil || interfaceMethod(fn) {
return nil
}
return fn
}
// usedIdent is the implementation of [internal/typesinternal.UsedIdent].
// It returns the identifier associated with e.
// See typesinternal.UsedIdent for a fuller description.
// This function should live in typesinternal, but cannot because it would
// create an import cycle.
//
//go:linkname usedIdent golang.org/x/tools/go/types/typeutil.usedIdent
func usedIdent(info *types.Info, e ast.Expr) *ast.Ident {
if info.Types == nil || info.Uses == nil {
panic("one of info.Types or info.Uses is nil; both must be populated")
}
// Look through type instantiation if necessary.
switch d := ast.Unparen(e).(type) {
case *ast.IndexExpr:
if info.Types[d.Index].IsType() {
e = d.X
}
case *ast.IndexListExpr:
e = d.X
}
switch e := ast.Unparen(e).(type) {
// info.Uses always has the object we want, even for selector expressions.
// We don't need info.Selections.
// See go/types/recording.go:recordSelection.
case *ast.Ident:
return e
case *ast.SelectorExpr:
return e.Sel
}
return nil
}
// interfaceMethod reports whether its argument is a method of an interface.
// This function should live in typesinternal, but cannot because it would create an import cycle.
//
//go:linkname interfaceMethod golang.org/x/tools/go/types/typeutil.interfaceMethod
func interfaceMethod(f *types.Func) bool {
recv := f.Signature().Recv()
return recv != nil && types.IsInterface(recv.Type())
}

30
vendor/golang.org/x/tools/go/types/typeutil/imports.go generated vendored Normal file
View File

@@ -0,0 +1,30 @@
// Copyright 2014 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 typeutil
import "go/types"
// Dependencies returns all dependencies of the specified packages.
//
// Dependent packages appear in topological order: if package P imports
// package Q, Q appears earlier than P in the result.
// The algorithm follows import statements in the order they
// appear in the source code, so the result is a total order.
func Dependencies(pkgs ...*types.Package) []*types.Package {
var result []*types.Package
seen := make(map[*types.Package]bool)
var visit func(pkgs []*types.Package)
visit = func(pkgs []*types.Package) {
for _, p := range pkgs {
if !seen[p] {
seen[p] = true
visit(p.Imports())
result = append(result, p)
}
}
}
visit(pkgs)
return result
}

459
vendor/golang.org/x/tools/go/types/typeutil/map.go generated vendored Normal file
View File

@@ -0,0 +1,459 @@
// Copyright 2014 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 typeutil defines various utilities for types, such as [Map],
// a hash table that maps [types.Type] to any value.
package typeutil
import (
"bytes"
"fmt"
"go/types"
"hash/maphash"
"golang.org/x/tools/internal/typeparams"
)
// Map is a hash-table-based mapping from types (types.Type) to
// arbitrary values. The concrete types that implement
// the Type interface are pointers. Since they are not canonicalized,
// == cannot be used to check for equivalence, and thus we cannot
// simply use a Go map.
//
// Just as with map[K]V, a nil *Map is a valid empty map.
//
// Read-only map operations ([Map.At], [Map.Len], and so on) may
// safely be called concurrently.
//
// TODO(adonovan): deprecate in favor of https://go.dev/issues/69420
// and 69559, if the latter proposals for a generic hash-map type and
// a types.Hash function are accepted.
type Map struct {
table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
length int // number of map entries
}
// entry is an entry (key/value association) in a hash bucket.
type entry struct {
key types.Type
value any
}
// SetHasher has no effect.
//
// It is a relic of an optimization that is no longer profitable. Do
// not use [Hasher], [MakeHasher], or [SetHasher] in new code.
func (m *Map) SetHasher(Hasher) {}
// Delete removes the entry with the given key, if any.
// It returns true if the entry was found.
func (m *Map) Delete(key types.Type) bool {
if m != nil && m.table != nil {
hash := hash(key)
bucket := m.table[hash]
for i, e := range bucket {
if e.key != nil && types.Identical(key, e.key) {
// We can't compact the bucket as it
// would disturb iterators.
bucket[i] = entry{}
m.length--
return true
}
}
}
return false
}
// At returns the map entry for the given key.
// The result is nil if the entry is not present.
func (m *Map) At(key types.Type) any {
if m != nil && m.table != nil {
for _, e := range m.table[hash(key)] {
if e.key != nil && types.Identical(key, e.key) {
return e.value
}
}
}
return nil
}
// Set sets the map entry for key to val,
// and returns the previous entry, if any.
func (m *Map) Set(key types.Type, value any) (prev any) {
if m.table != nil {
hash := hash(key)
bucket := m.table[hash]
var hole *entry
for i, e := range bucket {
if e.key == nil {
hole = &bucket[i]
} else if types.Identical(key, e.key) {
prev = e.value
bucket[i].value = value
return
}
}
if hole != nil {
*hole = entry{key, value} // overwrite deleted entry
} else {
m.table[hash] = append(bucket, entry{key, value})
}
} else {
hash := hash(key)
m.table = map[uint32][]entry{hash: {entry{key, value}}}
}
m.length++
return
}
// Len returns the number of map entries.
func (m *Map) Len() int {
if m != nil {
return m.length
}
return 0
}
// Iterate calls function f on each entry in the map in unspecified order.
//
// If f should mutate the map, Iterate provides the same guarantees as
// Go maps: if f deletes a map entry that Iterate has not yet reached,
// f will not be invoked for it, but if f inserts a map entry that
// Iterate has not yet reached, whether or not f will be invoked for
// it is unspecified.
func (m *Map) Iterate(f func(key types.Type, value any)) {
if m != nil {
for _, bucket := range m.table {
for _, e := range bucket {
if e.key != nil {
f(e.key, e.value)
}
}
}
}
}
// Keys returns a new slice containing the set of map keys.
// The order is unspecified.
func (m *Map) Keys() []types.Type {
keys := make([]types.Type, 0, m.Len())
m.Iterate(func(key types.Type, _ any) {
keys = append(keys, key)
})
return keys
}
func (m *Map) toString(values bool) string {
if m == nil {
return "{}"
}
var buf bytes.Buffer
fmt.Fprint(&buf, "{")
sep := ""
m.Iterate(func(key types.Type, value any) {
fmt.Fprint(&buf, sep)
sep = ", "
fmt.Fprint(&buf, key)
if values {
fmt.Fprintf(&buf, ": %q", value)
}
})
fmt.Fprint(&buf, "}")
return buf.String()
}
// String returns a string representation of the map's entries.
// Values are printed using fmt.Sprintf("%v", v).
// Order is unspecified.
func (m *Map) String() string {
return m.toString(true)
}
// KeysString returns a string representation of the map's key set.
// Order is unspecified.
func (m *Map) KeysString() string {
return m.toString(false)
}
// -- Hasher --
// hash returns the hash of type t.
// TODO(adonovan): replace by types.Hash when Go proposal #69420 is accepted.
func hash(t types.Type) uint32 {
return theHasher.Hash(t)
}
// A Hasher provides a [Hasher.Hash] method to map a type to its hash value.
// Hashers are stateless, and all are equivalent.
type Hasher struct{}
var theHasher Hasher
// MakeHasher returns Hasher{}.
// Hashers are stateless; all are equivalent.
func MakeHasher() Hasher { return theHasher }
// Hash computes a hash value for the given type t such that
// Identical(t, t') => Hash(t) == Hash(t').
func (h Hasher) Hash(t types.Type) uint32 {
return hasher{inGenericSig: false}.hash(t)
}
// hasher holds the state of a single Hash traversal: whether we are
// inside the signature of a generic function; this is used to
// optimize [hasher.hashTypeParam].
type hasher struct{ inGenericSig bool }
// hashString computes the FowlerNollVo hash of s.
func hashString(s string) uint32 {
var h uint32
for i := 0; i < len(s); i++ {
h ^= uint32(s[i])
h *= 16777619
}
return h
}
// hash computes the hash of t.
func (h hasher) hash(t types.Type) uint32 {
// See Identical for rationale.
switch t := t.(type) {
case *types.Basic:
return uint32(t.Kind())
case *types.Alias:
return h.hash(types.Unalias(t))
case *types.Array:
return 9043 + 2*uint32(t.Len()) + 3*h.hash(t.Elem())
case *types.Slice:
return 9049 + 2*h.hash(t.Elem())
case *types.Struct:
var hash uint32 = 9059
for i, n := 0, t.NumFields(); i < n; i++ {
f := t.Field(i)
if f.Anonymous() {
hash += 8861
}
hash += hashString(t.Tag(i))
hash += hashString(f.Name()) // (ignore f.Pkg)
hash += h.hash(f.Type())
}
return hash
case *types.Pointer:
return 9067 + 2*h.hash(t.Elem())
case *types.Signature:
var hash uint32 = 9091
if t.Variadic() {
hash *= 8863
}
tparams := t.TypeParams()
if n := tparams.Len(); n > 0 {
h.inGenericSig = true // affects constraints, params, and results
for i := range n {
tparam := tparams.At(i)
hash += 7 * h.hash(tparam.Constraint())
}
}
return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
case *types.Union:
return h.hashUnion(t)
case *types.Interface:
// Interfaces are identical if they have the same set of methods, with
// identical names and types, and they have the same set of type
// restrictions. See go/types.identical for more details.
var hash uint32 = 9103
// Hash methods.
for i, n := 0, t.NumMethods(); i < n; i++ {
// Method order is not significant.
// Ignore m.Pkg().
m := t.Method(i)
// Use shallow hash on method signature to
// avoid anonymous interface cycles.
hash += 3*hashString(m.Name()) + 5*h.shallowHash(m.Type())
}
// Hash type restrictions.
terms, err := typeparams.InterfaceTermSet(t)
// if err != nil t has invalid type restrictions.
if err == nil {
hash += h.hashTermSet(terms)
}
return hash
case *types.Map:
return 9109 + 2*h.hash(t.Key()) + 3*h.hash(t.Elem())
case *types.Chan:
return 9127 + 2*uint32(t.Dir()) + 3*h.hash(t.Elem())
case *types.Named:
hash := h.hashTypeName(t.Obj())
targs := t.TypeArgs()
for targ := range targs.Types() {
hash += 2 * h.hash(targ)
}
return hash
case *types.TypeParam:
return h.hashTypeParam(t)
case *types.Tuple:
return h.hashTuple(t)
}
panic(fmt.Sprintf("%T: %v", t, t))
}
func (h hasher) hashTuple(tuple *types.Tuple) uint32 {
// See go/types.identicalTypes for rationale.
n := tuple.Len()
hash := 9137 + 2*uint32(n)
for i := range n {
hash += 3 * h.hash(tuple.At(i).Type())
}
return hash
}
func (h hasher) hashUnion(t *types.Union) uint32 {
// Hash type restrictions.
terms, err := typeparams.UnionTermSet(t)
// if err != nil t has invalid type restrictions. Fall back on a non-zero
// hash.
if err != nil {
return 9151
}
return h.hashTermSet(terms)
}
func (h hasher) hashTermSet(terms []*types.Term) uint32 {
hash := 9157 + 2*uint32(len(terms))
for _, term := range terms {
// term order is not significant.
termHash := h.hash(term.Type())
if term.Tilde() {
termHash *= 9161
}
hash += 3 * termHash
}
return hash
}
// hashTypeParam returns the hash of a type parameter.
func (h hasher) hashTypeParam(t *types.TypeParam) uint32 {
// Within the signature of a generic function, TypeParams are
// identical if they have the same index and constraint, so we
// hash them based on index.
//
// When we are outside a generic function, free TypeParams are
// identical iff they are the same object, so we can use a
// more discriminating hash consistent with object identity.
// This optimization saves [Map] about 4% when hashing all the
// types.Info.Types in the forward closure of net/http.
if !h.inGenericSig {
// Optimization: outside a generic function signature,
// use a more discrimating hash consistent with object identity.
return h.hashTypeName(t.Obj())
}
return 9173 + 3*uint32(t.Index())
}
var theSeed = maphash.MakeSeed()
// hashTypeName hashes the pointer of tname.
func (hasher) hashTypeName(tname *types.TypeName) uint32 {
// Since types.Identical uses == to compare TypeNames,
// the Hash function uses maphash.Comparable.
hash := maphash.Comparable(theSeed, tname)
return uint32(hash ^ (hash >> 32))
}
// shallowHash computes a hash of t without looking at any of its
// element Types, to avoid potential anonymous cycles in the types of
// interface methods.
//
// When an unnamed non-empty interface type appears anywhere among the
// arguments or results of an interface method, there is a potential
// for endless recursion. Consider:
//
// type X interface { m() []*interface { X } }
//
// The problem is that the Methods of the interface in m's result type
// include m itself; there is no mention of the named type X that
// might help us break the cycle.
// (See comment in go/types.identical, case *Interface, for more.)
func (h hasher) shallowHash(t types.Type) uint32 {
// t is the type of an interface method (Signature),
// its params or results (Tuples), or their immediate
// elements (mostly Slice, Pointer, Basic, Named),
// so there's no need to optimize anything else.
switch t := t.(type) {
case *types.Alias:
return h.shallowHash(types.Unalias(t))
case *types.Signature:
var hash uint32 = 604171
if t.Variadic() {
hash *= 971767
}
// The Signature/Tuple recursion is always finite
// and invariably shallow.
return hash + 1062599*h.shallowHash(t.Params()) + 1282529*h.shallowHash(t.Results())
case *types.Tuple:
n := t.Len()
hash := 9137 + 2*uint32(n)
for i := range n {
hash += 53471161 * h.shallowHash(t.At(i).Type())
}
return hash
case *types.Basic:
return 45212177 * uint32(t.Kind())
case *types.Array:
return 1524181 + 2*uint32(t.Len())
case *types.Slice:
return 2690201
case *types.Struct:
return 3326489
case *types.Pointer:
return 4393139
case *types.Union:
return 562448657
case *types.Interface:
return 2124679 // no recursion here
case *types.Map:
return 9109
case *types.Chan:
return 9127
case *types.Named:
return h.hashTypeName(t.Obj())
case *types.TypeParam:
return h.hashTypeParam(t)
}
panic(fmt.Sprintf("shallowHash: %T: %v", t, t))
}

View File

@@ -0,0 +1,71 @@
// Copyright 2014 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.
// This file implements a cache of method sets.
package typeutil
import (
"go/types"
"sync"
)
// A MethodSetCache records the method set of each type T for which
// MethodSet(T) is called so that repeat queries are fast.
// The zero value is a ready-to-use cache instance.
type MethodSetCache struct {
mu sync.Mutex
named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N
others map[types.Type]*types.MethodSet // all other types
}
// MethodSet returns the method set of type T. It is thread-safe.
//
// If cache is nil, this function is equivalent to types.NewMethodSet(T).
// Utility functions can thus expose an optional *MethodSetCache
// parameter to clients that care about performance.
func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
if cache == nil {
return types.NewMethodSet(T)
}
cache.mu.Lock()
defer cache.mu.Unlock()
switch T := types.Unalias(T).(type) {
case *types.Named:
return cache.lookupNamed(T).value
case *types.Pointer:
if N, ok := types.Unalias(T.Elem()).(*types.Named); ok {
return cache.lookupNamed(N).pointer
}
}
// all other types
// (The map uses pointer equivalence, not type identity.)
mset := cache.others[T]
if mset == nil {
mset = types.NewMethodSet(T)
if cache.others == nil {
cache.others = make(map[types.Type]*types.MethodSet)
}
cache.others[T] = mset
}
return mset
}
func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
if cache.named == nil {
cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
}
// Avoid recomputing mset(*T) for each distinct Pointer
// instance whose underlying type is a named type.
msets, ok := cache.named[named]
if !ok {
msets.value = types.NewMethodSet(named)
msets.pointer = types.NewMethodSet(types.NewPointer(named))
cache.named[named] = msets
}
return msets
}

53
vendor/golang.org/x/tools/go/types/typeutil/ui.go generated vendored Normal file
View File

@@ -0,0 +1,53 @@
// Copyright 2014 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 typeutil
// This file defines utilities for user interfaces that display types.
import (
"go/types"
)
// IntuitiveMethodSet returns the intuitive method set of a type T,
// which is the set of methods you can call on an addressable value of
// that type.
//
// The result always contains MethodSet(T), and is exactly MethodSet(T)
// for interface types and for pointer-to-concrete types.
// For all other concrete types T, the result additionally
// contains each method belonging to *T if there is no identically
// named method on T itself.
//
// This corresponds to user intuition about method sets;
// this function is intended only for user interfaces.
//
// The order of the result is as for types.MethodSet(T).
func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
isPointerToConcrete := func(T types.Type) bool {
ptr, ok := types.Unalias(T).(*types.Pointer)
return ok && !types.IsInterface(ptr.Elem())
}
var result []*types.Selection
mset := msets.MethodSet(T)
if types.IsInterface(T) || isPointerToConcrete(T) {
for i, n := 0, mset.Len(); i < n; i++ {
result = append(result, mset.At(i))
}
} else {
// T is some other concrete type.
// Report methods of T and *T, preferring those of T.
pmset := msets.MethodSet(types.NewPointer(T))
for i, n := 0, pmset.Len(); i < n; i++ {
meth := pmset.At(i)
if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
meth = m
}
result = append(result, meth)
}
}
return result
}

38
vendor/golang.org/x/tools/internal/aliases/aliases.go generated vendored Normal file
View File

@@ -0,0 +1,38 @@
// 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.
package aliases
import (
"go/token"
"go/types"
)
// Package aliases defines backward compatible shims
// for the types.Alias type representation added in 1.22.
// This defines placeholders for x/tools until 1.26.
// NewAlias creates a new TypeName in Package pkg that
// is an alias for the type rhs.
//
// The enabled parameter determines whether the resulting [TypeName]'s
// type is an [types.Alias]. Its value must be the result of a call to
// [Enabled], which computes the effective value of
// GODEBUG=gotypesalias=... by invoking the type checker. The Enabled
// function is expensive and should be called once per task (e.g.
// package import), not once per call to NewAlias.
//
// Precondition: enabled || len(tparams)==0.
// If materialized aliases are disabled, there must not be any type parameters.
func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type, tparams []*types.TypeParam) *types.TypeName {
if enabled {
tname := types.NewTypeName(pos, pkg, name, nil)
SetTypeParams(types.NewAlias(tname, rhs), tparams)
return tname
}
if len(tparams) > 0 {
panic("cannot create an alias with type parameters when gotypesalias is not enabled")
}
return types.NewTypeName(pos, pkg, name, rhs)
}

View File

@@ -0,0 +1,80 @@
// 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.
package aliases
import (
"go/ast"
"go/parser"
"go/token"
"go/types"
)
// Rhs returns the type on the right-hand side of the alias declaration.
func Rhs(alias *types.Alias) types.Type {
if alias, ok := any(alias).(interface{ Rhs() types.Type }); ok {
return alias.Rhs() // go1.23+
}
// go1.22's Alias didn't have the Rhs method,
// so Unalias is the best we can do.
return types.Unalias(alias)
}
// TypeParams returns the type parameter list of the alias.
func TypeParams(alias *types.Alias) *types.TypeParamList {
if alias, ok := any(alias).(interface{ TypeParams() *types.TypeParamList }); ok {
return alias.TypeParams() // go1.23+
}
return nil
}
// SetTypeParams sets the type parameters of the alias type.
func SetTypeParams(alias *types.Alias, tparams []*types.TypeParam) {
if alias, ok := any(alias).(interface {
SetTypeParams(tparams []*types.TypeParam)
}); ok {
alias.SetTypeParams(tparams) // go1.23+
} else if len(tparams) > 0 {
panic("cannot set type parameters of an Alias type in go1.22")
}
}
// TypeArgs returns the type arguments used to instantiate the Alias type.
func TypeArgs(alias *types.Alias) *types.TypeList {
if alias, ok := any(alias).(interface{ TypeArgs() *types.TypeList }); ok {
return alias.TypeArgs() // go1.23+
}
return nil // empty (go1.22)
}
// Origin returns the generic Alias type of which alias is an instance.
// If alias is not an instance of a generic alias, Origin returns alias.
func Origin(alias *types.Alias) *types.Alias {
if alias, ok := any(alias).(interface{ Origin() *types.Alias }); ok {
return alias.Origin() // go1.23+
}
return alias // not an instance of a generic alias (go1.22)
}
// Enabled reports whether [NewAlias] should create [types.Alias] types.
//
// This function is expensive! Call it sparingly.
func Enabled() bool {
// The only reliable way to compute the answer is to invoke go/types.
// We don't parse the GODEBUG environment variable, because
// (a) it's tricky to do so in a manner that is consistent
// with the godebug package; in particular, a simple
// substring check is not good enough. The value is a
// rightmost-wins list of options. But more importantly:
// (b) it is impossible to detect changes to the effective
// setting caused by os.Setenv("GODEBUG"), as happens in
// many tests. Therefore any attempt to cache the result
// is just incorrect.
fset := token.NewFileSet()
f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", parser.SkipObjectResolution)
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
_, enabled := pkg.Scope().Lookup("A").Type().(*types.Alias)
return enabled
}

80
vendor/golang.org/x/tools/internal/event/core/event.go generated vendored Normal file
View File

@@ -0,0 +1,80 @@
// Copyright 2019 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 core provides support for event based telemetry.
package core
import (
"fmt"
"time"
"golang.org/x/tools/internal/event/label"
)
// Event holds the information about an event of note that occurred.
type Event struct {
at time.Time
// As events are often on the stack, storing the first few labels directly
// in the event can avoid an allocation at all for the very common cases of
// simple events.
// The length needs to be large enough to cope with the majority of events
// but no so large as to cause undue stack pressure.
// A log message with two values will use 3 labels (one for each value and
// one for the message itself).
static [3]label.Label // inline storage for the first few labels
dynamic []label.Label // dynamically sized storage for remaining labels
}
func (ev Event) At() time.Time { return ev.at }
func (ev Event) Format(f fmt.State, r rune) {
if !ev.at.IsZero() {
fmt.Fprint(f, ev.at.Format("2006/01/02 15:04:05 "))
}
for index := 0; ev.Valid(index); index++ {
if l := ev.Label(index); l.Valid() {
fmt.Fprintf(f, "\n\t%v", l)
}
}
}
func (ev Event) Valid(index int) bool {
return index >= 0 && index < len(ev.static)+len(ev.dynamic)
}
func (ev Event) Label(index int) label.Label {
if index < len(ev.static) {
return ev.static[index]
}
return ev.dynamic[index-len(ev.static)]
}
func (ev Event) Find(key label.Key) label.Label {
for _, l := range ev.static {
if l.Key() == key {
return l
}
}
for _, l := range ev.dynamic {
if l.Key() == key {
return l
}
}
return label.Label{}
}
func MakeEvent(static [3]label.Label, labels []label.Label) Event {
return Event{
static: static,
dynamic: labels,
}
}
// CloneEvent event returns a copy of the event with the time adjusted to at.
func CloneEvent(ev Event, at time.Time) Event {
ev.at = at
return ev
}

View File

@@ -0,0 +1,67 @@
// Copyright 2019 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 core
import (
"context"
"sync/atomic"
"time"
"golang.org/x/tools/internal/event/label"
)
// Exporter is a function that handles events.
// It may return a modified context and event.
type Exporter func(context.Context, Event, label.Map) context.Context
var exporter atomic.Pointer[Exporter]
// SetExporter sets the global exporter function that handles all events.
// The exporter is called synchronously from the event call site, so it should
// return quickly so as not to hold up user code.
func SetExporter(e Exporter) {
if e == nil {
// &e is always valid, and so p is always valid, but for the early abort
// of ProcessEvent to be efficient it needs to make the nil check on the
// pointer without having to dereference it, so we make the nil function
// also a nil pointer
exporter.Store(nil)
} else {
exporter.Store(&e)
}
}
// deliver is called to deliver an event to the supplied exporter.
// it will fill in the time.
func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context {
// add the current time to the event
ev.at = time.Now()
// hand the event off to the current exporter
return exporter(ctx, ev, ev)
}
// Export is called to deliver an event to the global exporter if set.
func Export(ctx context.Context, ev Event) context.Context {
// get the global exporter and abort early if there is not one
exporterPtr := exporter.Load()
if exporterPtr == nil {
return ctx
}
return deliver(ctx, *exporterPtr, ev)
}
// ExportPair is called to deliver a start event to the supplied exporter.
// It also returns a function that will deliver the end event to the same
// exporter.
// It will fill in the time.
func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) {
// get the global exporter and abort early if there is not one
exporterPtr := exporter.Load()
if exporterPtr == nil {
return ctx, func() {}
}
ctx = deliver(ctx, *exporterPtr, begin)
return ctx, func() { deliver(ctx, *exporterPtr, end) }
}

77
vendor/golang.org/x/tools/internal/event/core/fast.go generated vendored Normal file
View File

@@ -0,0 +1,77 @@
// Copyright 2019 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 core
import (
"context"
"golang.org/x/tools/internal/event/keys"
"golang.org/x/tools/internal/event/label"
)
// Log1 takes a message and one label delivers a log event to the exporter.
// It is a customized version of Print that is faster and does no allocation.
func Log1(ctx context.Context, message string, t1 label.Label) {
Export(ctx, MakeEvent([3]label.Label{
keys.Msg.Of(message),
t1,
}, nil))
}
// Log2 takes a message and two labels and delivers a log event to the exporter.
// It is a customized version of Print that is faster and does no allocation.
func Log2(ctx context.Context, message string, t1 label.Label, t2 label.Label) {
Export(ctx, MakeEvent([3]label.Label{
keys.Msg.Of(message),
t1,
t2,
}, nil))
}
// Metric1 sends a label event to the exporter with the supplied labels.
func Metric1(ctx context.Context, t1 label.Label) context.Context {
return Export(ctx, MakeEvent([3]label.Label{
keys.Metric.New(),
t1,
}, nil))
}
// Metric2 sends a label event to the exporter with the supplied labels.
func Metric2(ctx context.Context, t1, t2 label.Label) context.Context {
return Export(ctx, MakeEvent([3]label.Label{
keys.Metric.New(),
t1,
t2,
}, nil))
}
// Start1 sends a span start event with the supplied label list to the exporter.
// It also returns a function that will end the span, which should normally be
// deferred.
func Start1(ctx context.Context, name string, t1 label.Label) (context.Context, func()) {
return ExportPair(ctx,
MakeEvent([3]label.Label{
keys.Start.Of(name),
t1,
}, nil),
MakeEvent([3]label.Label{
keys.End.New(),
}, nil))
}
// Start2 sends a span start event with the supplied label list to the exporter.
// It also returns a function that will end the span, which should normally be
// deferred.
func Start2(ctx context.Context, name string, t1, t2 label.Label) (context.Context, func()) {
return ExportPair(ctx,
MakeEvent([3]label.Label{
keys.Start.Of(name),
t1,
t2,
}, nil),
MakeEvent([3]label.Label{
keys.End.New(),
}, nil))
}

7
vendor/golang.org/x/tools/internal/event/doc.go generated vendored Normal file
View File

@@ -0,0 +1,7 @@
// Copyright 2019 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 event provides a set of packages that cover the main
// concepts of telemetry in an implementation agnostic way.
package event

127
vendor/golang.org/x/tools/internal/event/event.go generated vendored Normal file
View File

@@ -0,0 +1,127 @@
// Copyright 2019 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 event
import (
"context"
"golang.org/x/tools/internal/event/core"
"golang.org/x/tools/internal/event/keys"
"golang.org/x/tools/internal/event/label"
)
// Exporter is a function that handles events.
// It may return a modified context and event.
type Exporter func(context.Context, core.Event, label.Map) context.Context
// SetExporter sets the global exporter function that handles all events.
// The exporter is called synchronously from the event call site, so it should
// return quickly so as not to hold up user code.
func SetExporter(e Exporter) {
core.SetExporter(core.Exporter(e))
}
// Log takes a message and a label list and combines them into a single event
// before delivering them to the exporter.
func Log(ctx context.Context, message string, labels ...label.Label) {
core.Export(ctx, core.MakeEvent([3]label.Label{
keys.Msg.Of(message),
}, labels))
}
// IsLog returns true if the event was built by the Log function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsLog(ev core.Event) bool {
return ev.Label(0).Key() == keys.Msg
}
// Error takes a message and a label list and combines them into a single event
// before delivering them to the exporter. It captures the error in the
// delivered event.
func Error(ctx context.Context, message string, err error, labels ...label.Label) {
core.Export(ctx, core.MakeEvent([3]label.Label{
keys.Msg.Of(message),
keys.Err.Of(err),
}, labels))
}
// IsError returns true if the event was built by the Error function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsError(ev core.Event) bool {
return ev.Label(0).Key() == keys.Msg &&
ev.Label(1).Key() == keys.Err
}
// Metric sends a label event to the exporter with the supplied labels.
func Metric(ctx context.Context, labels ...label.Label) {
core.Export(ctx, core.MakeEvent([3]label.Label{
keys.Metric.New(),
}, labels))
}
// IsMetric returns true if the event was built by the Metric function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsMetric(ev core.Event) bool {
return ev.Label(0).Key() == keys.Metric
}
// Label sends a label event to the exporter with the supplied labels.
func Label(ctx context.Context, labels ...label.Label) context.Context {
return core.Export(ctx, core.MakeEvent([3]label.Label{
keys.Label.New(),
}, labels))
}
// IsLabel returns true if the event was built by the Label function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsLabel(ev core.Event) bool {
return ev.Label(0).Key() == keys.Label
}
// Start sends a span start event with the supplied label list to the exporter.
// It also returns a function that will end the span, which should normally be
// deferred.
func Start(ctx context.Context, name string, labels ...label.Label) (context.Context, func()) {
return core.ExportPair(ctx,
core.MakeEvent([3]label.Label{
keys.Start.Of(name),
}, labels),
core.MakeEvent([3]label.Label{
keys.End.New(),
}, nil))
}
// IsStart returns true if the event was built by the Start function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsStart(ev core.Event) bool {
return ev.Label(0).Key() == keys.Start
}
// IsEnd returns true if the event was built by the End function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsEnd(ev core.Event) bool {
return ev.Label(0).Key() == keys.End
}
// Detach returns a context without an associated span.
// This allows the creation of spans that are not children of the current span.
func Detach(ctx context.Context) context.Context {
return core.Export(ctx, core.MakeEvent([3]label.Label{
keys.Detach.New(),
}, nil))
}
// IsDetach returns true if the event was built by the Detach function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsDetach(ev core.Event) bool {
return ev.Label(0).Key() == keys.Detach
}

564
vendor/golang.org/x/tools/internal/event/keys/keys.go generated vendored Normal file
View File

@@ -0,0 +1,564 @@
// Copyright 2019 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 keys
import (
"fmt"
"io"
"math"
"strconv"
"golang.org/x/tools/internal/event/label"
)
// Value represents a key for untyped values.
type Value struct {
name string
description string
}
// New creates a new Key for untyped values.
func New(name, description string) *Value {
return &Value{name: name, description: description}
}
func (k *Value) Name() string { return k.name }
func (k *Value) Description() string { return k.description }
func (k *Value) Format(w io.Writer, buf []byte, l label.Label) {
fmt.Fprint(w, k.From(l))
}
// Get can be used to get a label for the key from a label.Map.
func (k *Value) Get(lm label.Map) any {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return nil
}
// From can be used to get a value from a Label.
func (k *Value) From(t label.Label) any { return t.UnpackValue() }
// Of creates a new Label with this key and the supplied value.
func (k *Value) Of(value any) label.Label { return label.OfValue(k, value) }
// Tag represents a key for tagging labels that have no value.
// These are used when the existence of the label is the entire information it
// carries, such as marking events to be of a specific kind, or from a specific
// package.
type Tag struct {
name string
description string
}
// NewTag creates a new Key for tagging labels.
func NewTag(name, description string) *Tag {
return &Tag{name: name, description: description}
}
func (k *Tag) Name() string { return k.name }
func (k *Tag) Description() string { return k.description }
func (k *Tag) Format(w io.Writer, buf []byte, l label.Label) {}
// New creates a new Label with this key.
func (k *Tag) New() label.Label { return label.OfValue(k, nil) }
// Int represents a key
type Int struct {
name string
description string
}
// NewInt creates a new Key for int values.
func NewInt(name, description string) *Int {
return &Int{name: name, description: description}
}
func (k *Int) Name() string { return k.name }
func (k *Int) Description() string { return k.description }
func (k *Int) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
}
// Of creates a new Label with this key and the supplied value.
func (k *Int) Of(v int) label.Label { return label.Of64(k, uint64(v)) }
// Get can be used to get a label for the key from a label.Map.
func (k *Int) Get(lm label.Map) int {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return 0
}
// From can be used to get a value from a Label.
func (k *Int) From(t label.Label) int { return int(t.Unpack64()) }
// Int8 represents a key
type Int8 struct {
name string
description string
}
// NewInt8 creates a new Key for int8 values.
func NewInt8(name, description string) *Int8 {
return &Int8{name: name, description: description}
}
func (k *Int8) Name() string { return k.name }
func (k *Int8) Description() string { return k.description }
func (k *Int8) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
}
// Of creates a new Label with this key and the supplied value.
func (k *Int8) Of(v int8) label.Label { return label.Of64(k, uint64(v)) }
// Get can be used to get a label for the key from a label.Map.
func (k *Int8) Get(lm label.Map) int8 {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return 0
}
// From can be used to get a value from a Label.
func (k *Int8) From(t label.Label) int8 { return int8(t.Unpack64()) }
// Int16 represents a key
type Int16 struct {
name string
description string
}
// NewInt16 creates a new Key for int16 values.
func NewInt16(name, description string) *Int16 {
return &Int16{name: name, description: description}
}
func (k *Int16) Name() string { return k.name }
func (k *Int16) Description() string { return k.description }
func (k *Int16) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
}
// Of creates a new Label with this key and the supplied value.
func (k *Int16) Of(v int16) label.Label { return label.Of64(k, uint64(v)) }
// Get can be used to get a label for the key from a label.Map.
func (k *Int16) Get(lm label.Map) int16 {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return 0
}
// From can be used to get a value from a Label.
func (k *Int16) From(t label.Label) int16 { return int16(t.Unpack64()) }
// Int32 represents a key
type Int32 struct {
name string
description string
}
// NewInt32 creates a new Key for int32 values.
func NewInt32(name, description string) *Int32 {
return &Int32{name: name, description: description}
}
func (k *Int32) Name() string { return k.name }
func (k *Int32) Description() string { return k.description }
func (k *Int32) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
}
// Of creates a new Label with this key and the supplied value.
func (k *Int32) Of(v int32) label.Label { return label.Of64(k, uint64(v)) }
// Get can be used to get a label for the key from a label.Map.
func (k *Int32) Get(lm label.Map) int32 {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return 0
}
// From can be used to get a value from a Label.
func (k *Int32) From(t label.Label) int32 { return int32(t.Unpack64()) }
// Int64 represents a key
type Int64 struct {
name string
description string
}
// NewInt64 creates a new Key for int64 values.
func NewInt64(name, description string) *Int64 {
return &Int64{name: name, description: description}
}
func (k *Int64) Name() string { return k.name }
func (k *Int64) Description() string { return k.description }
func (k *Int64) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendInt(buf, k.From(l), 10))
}
// Of creates a new Label with this key and the supplied value.
func (k *Int64) Of(v int64) label.Label { return label.Of64(k, uint64(v)) }
// Get can be used to get a label for the key from a label.Map.
func (k *Int64) Get(lm label.Map) int64 {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return 0
}
// From can be used to get a value from a Label.
func (k *Int64) From(t label.Label) int64 { return int64(t.Unpack64()) }
// UInt represents a key
type UInt struct {
name string
description string
}
// NewUInt creates a new Key for uint values.
func NewUInt(name, description string) *UInt {
return &UInt{name: name, description: description}
}
func (k *UInt) Name() string { return k.name }
func (k *UInt) Description() string { return k.description }
func (k *UInt) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
}
// Of creates a new Label with this key and the supplied value.
func (k *UInt) Of(v uint) label.Label { return label.Of64(k, uint64(v)) }
// Get can be used to get a label for the key from a label.Map.
func (k *UInt) Get(lm label.Map) uint {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return 0
}
// From can be used to get a value from a Label.
func (k *UInt) From(t label.Label) uint { return uint(t.Unpack64()) }
// UInt8 represents a key
type UInt8 struct {
name string
description string
}
// NewUInt8 creates a new Key for uint8 values.
func NewUInt8(name, description string) *UInt8 {
return &UInt8{name: name, description: description}
}
func (k *UInt8) Name() string { return k.name }
func (k *UInt8) Description() string { return k.description }
func (k *UInt8) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
}
// Of creates a new Label with this key and the supplied value.
func (k *UInt8) Of(v uint8) label.Label { return label.Of64(k, uint64(v)) }
// Get can be used to get a label for the key from a label.Map.
func (k *UInt8) Get(lm label.Map) uint8 {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return 0
}
// From can be used to get a value from a Label.
func (k *UInt8) From(t label.Label) uint8 { return uint8(t.Unpack64()) }
// UInt16 represents a key
type UInt16 struct {
name string
description string
}
// NewUInt16 creates a new Key for uint16 values.
func NewUInt16(name, description string) *UInt16 {
return &UInt16{name: name, description: description}
}
func (k *UInt16) Name() string { return k.name }
func (k *UInt16) Description() string { return k.description }
func (k *UInt16) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
}
// Of creates a new Label with this key and the supplied value.
func (k *UInt16) Of(v uint16) label.Label { return label.Of64(k, uint64(v)) }
// Get can be used to get a label for the key from a label.Map.
func (k *UInt16) Get(lm label.Map) uint16 {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return 0
}
// From can be used to get a value from a Label.
func (k *UInt16) From(t label.Label) uint16 { return uint16(t.Unpack64()) }
// UInt32 represents a key
type UInt32 struct {
name string
description string
}
// NewUInt32 creates a new Key for uint32 values.
func NewUInt32(name, description string) *UInt32 {
return &UInt32{name: name, description: description}
}
func (k *UInt32) Name() string { return k.name }
func (k *UInt32) Description() string { return k.description }
func (k *UInt32) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
}
// Of creates a new Label with this key and the supplied value.
func (k *UInt32) Of(v uint32) label.Label { return label.Of64(k, uint64(v)) }
// Get can be used to get a label for the key from a label.Map.
func (k *UInt32) Get(lm label.Map) uint32 {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return 0
}
// From can be used to get a value from a Label.
func (k *UInt32) From(t label.Label) uint32 { return uint32(t.Unpack64()) }
// UInt64 represents a key
type UInt64 struct {
name string
description string
}
// NewUInt64 creates a new Key for uint64 values.
func NewUInt64(name, description string) *UInt64 {
return &UInt64{name: name, description: description}
}
func (k *UInt64) Name() string { return k.name }
func (k *UInt64) Description() string { return k.description }
func (k *UInt64) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendUint(buf, k.From(l), 10))
}
// Of creates a new Label with this key and the supplied value.
func (k *UInt64) Of(v uint64) label.Label { return label.Of64(k, v) }
// Get can be used to get a label for the key from a label.Map.
func (k *UInt64) Get(lm label.Map) uint64 {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return 0
}
// From can be used to get a value from a Label.
func (k *UInt64) From(t label.Label) uint64 { return t.Unpack64() }
// Float32 represents a key
type Float32 struct {
name string
description string
}
// NewFloat32 creates a new Key for float32 values.
func NewFloat32(name, description string) *Float32 {
return &Float32{name: name, description: description}
}
func (k *Float32) Name() string { return k.name }
func (k *Float32) Description() string { return k.description }
func (k *Float32) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendFloat(buf, float64(k.From(l)), 'E', -1, 32))
}
// Of creates a new Label with this key and the supplied value.
func (k *Float32) Of(v float32) label.Label {
return label.Of64(k, uint64(math.Float32bits(v)))
}
// Get can be used to get a label for the key from a label.Map.
func (k *Float32) Get(lm label.Map) float32 {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return 0
}
// From can be used to get a value from a Label.
func (k *Float32) From(t label.Label) float32 {
return math.Float32frombits(uint32(t.Unpack64()))
}
// Float64 represents a key
type Float64 struct {
name string
description string
}
// NewFloat64 creates a new Key for int64 values.
func NewFloat64(name, description string) *Float64 {
return &Float64{name: name, description: description}
}
func (k *Float64) Name() string { return k.name }
func (k *Float64) Description() string { return k.description }
func (k *Float64) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendFloat(buf, k.From(l), 'E', -1, 64))
}
// Of creates a new Label with this key and the supplied value.
func (k *Float64) Of(v float64) label.Label {
return label.Of64(k, math.Float64bits(v))
}
// Get can be used to get a label for the key from a label.Map.
func (k *Float64) Get(lm label.Map) float64 {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return 0
}
// From can be used to get a value from a Label.
func (k *Float64) From(t label.Label) float64 {
return math.Float64frombits(t.Unpack64())
}
// String represents a key
type String struct {
name string
description string
}
// NewString creates a new Key for int64 values.
func NewString(name, description string) *String {
return &String{name: name, description: description}
}
func (k *String) Name() string { return k.name }
func (k *String) Description() string { return k.description }
func (k *String) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendQuote(buf, k.From(l)))
}
// Of creates a new Label with this key and the supplied value.
func (k *String) Of(v string) label.Label { return label.OfString(k, v) }
// Get can be used to get a label for the key from a label.Map.
func (k *String) Get(lm label.Map) string {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return ""
}
// From can be used to get a value from a Label.
func (k *String) From(t label.Label) string { return t.UnpackString() }
// Boolean represents a key
type Boolean struct {
name string
description string
}
// NewBoolean creates a new Key for bool values.
func NewBoolean(name, description string) *Boolean {
return &Boolean{name: name, description: description}
}
func (k *Boolean) Name() string { return k.name }
func (k *Boolean) Description() string { return k.description }
func (k *Boolean) Format(w io.Writer, buf []byte, l label.Label) {
w.Write(strconv.AppendBool(buf, k.From(l)))
}
// Of creates a new Label with this key and the supplied value.
func (k *Boolean) Of(v bool) label.Label {
if v {
return label.Of64(k, 1)
}
return label.Of64(k, 0)
}
// Get can be used to get a label for the key from a label.Map.
func (k *Boolean) Get(lm label.Map) bool {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return false
}
// From can be used to get a value from a Label.
func (k *Boolean) From(t label.Label) bool { return t.Unpack64() > 0 }
// Error represents a key
type Error struct {
name string
description string
}
// NewError creates a new Key for int64 values.
func NewError(name, description string) *Error {
return &Error{name: name, description: description}
}
func (k *Error) Name() string { return k.name }
func (k *Error) Description() string { return k.description }
func (k *Error) Format(w io.Writer, buf []byte, l label.Label) {
io.WriteString(w, k.From(l).Error())
}
// Of creates a new Label with this key and the supplied value.
func (k *Error) Of(v error) label.Label { return label.OfValue(k, v) }
// Get can be used to get a label for the key from a label.Map.
func (k *Error) Get(lm label.Map) error {
if t := lm.Find(k); t.Valid() {
return k.From(t)
}
return nil
}
// From can be used to get a value from a Label.
func (k *Error) From(t label.Label) error {
err, _ := t.UnpackValue().(error)
return err
}

View File

@@ -0,0 +1,22 @@
// Copyright 2020 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 keys
var (
// Msg is a key used to add message strings to label lists.
Msg = NewString("message", "a readable message")
// Label is a key used to indicate an event adds labels to the context.
Label = NewTag("label", "a label context marker")
// Start is used for things like traces that have a name.
Start = NewString("start", "span start")
// Metric is a key used to indicate an event records metrics.
End = NewTag("end", "a span end marker")
// Metric is a key used to indicate an event records metrics.
Detach = NewTag("detach", "a span detach marker")
// Err is a key used to add error values to label lists.
Err = NewError("error", "an error that occurred")
// Metric is a key used to indicate an event records metrics.
Metric = NewTag("metric", "a metric event marker")
)

21
vendor/golang.org/x/tools/internal/event/keys/util.go generated vendored Normal file
View File

@@ -0,0 +1,21 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package keys
import (
"sort"
"strings"
)
// Join returns a canonical join of the keys in S:
// a sorted comma-separated string list.
func Join[S ~[]T, T ~string](s S) string {
strs := make([]string, 0, len(s))
for _, v := range s {
strs = append(strs, string(v))
}
sort.Strings(strs)
return strings.Join(strs, ",")
}

208
vendor/golang.org/x/tools/internal/event/label/label.go generated vendored Normal file
View File

@@ -0,0 +1,208 @@
// Copyright 2019 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 label
import (
"fmt"
"io"
"slices"
"unsafe"
)
// Key is used as the identity of a Label.
// Keys are intended to be compared by pointer only, the name should be unique
// for communicating with external systems, but it is not required or enforced.
type Key interface {
// Name returns the key name.
Name() string
// Description returns a string that can be used to describe the value.
Description() string
// Format is used in formatting to append the value of the label to the
// supplied buffer.
// The formatter may use the supplied buf as a scratch area to avoid
// allocations.
Format(w io.Writer, buf []byte, l Label)
}
// Label holds a key and value pair.
// It is normally used when passing around lists of labels.
type Label struct {
key Key
packed uint64
untyped any
}
// Map is the interface to a collection of Labels indexed by key.
type Map interface {
// Find returns the label that matches the supplied key.
Find(key Key) Label
}
// List is the interface to something that provides an iterable
// list of labels.
// Iteration should start from 0 and continue until Valid returns false.
type List interface {
// Valid returns true if the index is within range for the list.
// It does not imply the label at that index will itself be valid.
Valid(index int) bool
// Label returns the label at the given index.
Label(index int) Label
}
// list implements LabelList for a list of Labels.
type list struct {
labels []Label
}
// filter wraps a LabelList filtering out specific labels.
type filter struct {
keys []Key
underlying List
}
// listMap implements LabelMap for a simple list of labels.
type listMap struct {
labels []Label
}
// mapChain implements LabelMap for a list of underlying LabelMap.
type mapChain struct {
maps []Map
}
// OfValue creates a new label from the key and value.
// This method is for implementing new key types, label creation should
// normally be done with the Of method of the key.
func OfValue(k Key, value any) Label { return Label{key: k, untyped: value} }
// UnpackValue assumes the label was built using LabelOfValue and returns the value
// that was passed to that constructor.
// This method is for implementing new key types, for type safety normal
// access should be done with the From method of the key.
func (t Label) UnpackValue() any { return t.untyped }
// Of64 creates a new label from a key and a uint64. This is often
// used for non uint64 values that can be packed into a uint64.
// This method is for implementing new key types, label creation should
// normally be done with the Of method of the key.
func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} }
// Unpack64 assumes the label was built using LabelOf64 and returns the value that
// was passed to that constructor.
// This method is for implementing new key types, for type safety normal
// access should be done with the From method of the key.
func (t Label) Unpack64() uint64 { return t.packed }
type stringptr unsafe.Pointer
// OfString creates a new label from a key and a string.
// This method is for implementing new key types, label creation should
// normally be done with the Of method of the key.
func OfString(k Key, v string) Label {
return Label{
key: k,
packed: uint64(len(v)),
untyped: stringptr(unsafe.StringData(v)),
}
}
// UnpackString assumes the label was built using LabelOfString and returns the
// value that was passed to that constructor.
// This method is for implementing new key types, for type safety normal
// access should be done with the From method of the key.
func (t Label) UnpackString() string {
return unsafe.String((*byte)(t.untyped.(stringptr)), int(t.packed))
}
// Valid returns true if the Label is a valid one (it has a key).
func (t Label) Valid() bool { return t.key != nil }
// Key returns the key of this Label.
func (t Label) Key() Key { return t.key }
// Format is used for debug printing of labels.
func (t Label) Format(f fmt.State, r rune) {
if !t.Valid() {
io.WriteString(f, `nil`)
return
}
io.WriteString(f, t.Key().Name())
io.WriteString(f, "=")
var buf [128]byte
t.Key().Format(f, buf[:0], t)
}
func (l *list) Valid(index int) bool {
return index >= 0 && index < len(l.labels)
}
func (l *list) Label(index int) Label {
return l.labels[index]
}
func (f *filter) Valid(index int) bool {
return f.underlying.Valid(index)
}
func (f *filter) Label(index int) Label {
l := f.underlying.Label(index)
if slices.Contains(f.keys, l.Key()) {
return Label{}
}
return l
}
func (lm listMap) Find(key Key) Label {
for _, l := range lm.labels {
if l.Key() == key {
return l
}
}
return Label{}
}
func (c mapChain) Find(key Key) Label {
for _, src := range c.maps {
l := src.Find(key)
if l.Valid() {
return l
}
}
return Label{}
}
var emptyList = &list{}
func NewList(labels ...Label) List {
if len(labels) == 0 {
return emptyList
}
return &list{labels: labels}
}
func Filter(l List, keys ...Key) List {
if len(keys) == 0 {
return l
}
return &filter{keys: keys, underlying: l}
}
func NewMap(labels ...Label) Map {
return listMap{labels: labels}
}
func MergeMaps(srcs ...Map) Map {
var nonNil []Map
for _, src := range srcs {
if src != nil {
nonNil = append(nonNil, src)
}
}
if len(nonNil) == 1 {
return nonNil[0]
}
return mapChain{maps: nonNil}
}

View File

@@ -0,0 +1,89 @@
// 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.
// This file contains the remaining vestiges of
// $GOROOT/src/go/internal/gcimporter/bimport.go.
package gcimporter
import (
"fmt"
"go/token"
"go/types"
"sync"
)
func errorf(format string, args ...any) {
panic(fmt.Sprintf(format, args...))
}
const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
// Synthesize a token.Pos
type fakeFileSet struct {
fset *token.FileSet
files map[string]*fileInfo
}
type fileInfo struct {
file *token.File
lastline int
}
const maxlines = 64 * 1024
func (s *fakeFileSet) pos(file string, line, column int) token.Pos {
_ = column // TODO(mdempsky): Make use of column.
// Since we don't know the set of needed file positions, we reserve maxlines
// positions per file. We delay calling token.File.SetLines until all
// positions have been calculated (by way of fakeFileSet.setLines), so that
// we can avoid setting unnecessary lines. See also golang/go#46586.
f := s.files[file]
if f == nil {
f = &fileInfo{file: s.fset.AddFile(file, -1, maxlines)}
s.files[file] = f
}
if line > maxlines {
line = 1
}
if line > f.lastline {
f.lastline = line
}
// Return a fake position assuming that f.file consists only of newlines.
return token.Pos(f.file.Base() + line - 1)
}
func (s *fakeFileSet) setLines() {
fakeLinesOnce.Do(func() {
fakeLines = make([]int, maxlines)
for i := range fakeLines {
fakeLines[i] = i
}
})
for _, f := range s.files {
f.file.SetLines(fakeLines[:f.lastline])
}
}
var (
fakeLines []int
fakeLinesOnce sync.Once
)
func chanDir(d int) types.ChanDir {
// tag values must match the constants in cmd/compile/internal/gc/go.go
switch d {
case 1 /* Crecv */ :
return types.RecvOnly
case 2 /* Csend */ :
return types.SendOnly
case 3 /* Cboth */ :
return types.SendRecv
default:
errorf("unexpected channel dir %d", d)
return 0
}
}

View File

@@ -0,0 +1,421 @@
// Copyright 2011 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.
// This file should be kept in sync with $GOROOT/src/internal/exportdata/exportdata.go.
// This file also additionally implements FindExportData for gcexportdata.NewReader.
package gcimporter
import (
"bufio"
"bytes"
"errors"
"fmt"
"go/build"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
)
// FindExportData positions the reader r at the beginning of the
// export data section of an underlying cmd/compile created archive
// file by reading from it. The reader must be positioned at the
// start of the file before calling this function.
// This returns the length of the export data in bytes.
//
// This function is needed by [gcexportdata.Read], which must
// accept inputs produced by the last two releases of cmd/compile,
// plus tip.
func FindExportData(r *bufio.Reader) (size int64, err error) {
arsize, err := FindPackageDefinition(r)
if err != nil {
return
}
size = int64(arsize)
objapi, headers, err := ReadObjectHeaders(r)
if err != nil {
return
}
size -= int64(len(objapi))
for _, h := range headers {
size -= int64(len(h))
}
// Check for the binary export data section header "$$B\n".
// TODO(taking): Unify with ReadExportDataHeader so that it stops at the 'u' instead of reading
line, err := r.ReadSlice('\n')
if err != nil {
return
}
hdr := string(line)
if hdr != "$$B\n" {
err = fmt.Errorf("unknown export data header: %q", hdr)
return
}
size -= int64(len(hdr))
// For files with a binary export data header "$$B\n",
// these are always terminated by an end-of-section marker "\n$$\n".
// So the last bytes must always be this constant.
//
// The end-of-section marker is not a part of the export data itself.
// Do not include these in size.
//
// It would be nice to have sanity check that the final bytes after
// the export data are indeed the end-of-section marker. The split
// of gcexportdata.NewReader and gcexportdata.Read make checking this
// ugly so gcimporter gives up enforcing this. The compiler and go/types
// importer do enforce this, which seems good enough.
const endofsection = "\n$$\n"
size -= int64(len(endofsection))
if size < 0 {
err = fmt.Errorf("invalid size (%d) in the archive file: %d bytes remain without section headers (recompile package)", arsize, size)
return
}
return
}
// ReadUnified reads the contents of the unified export data from a reader r
// that contains the contents of a GC-created archive file.
//
// On success, the reader will be positioned after the end-of-section marker "\n$$\n".
//
// Supported GC-created archive files have 4 layers of nesting:
// - An archive file containing a package definition file.
// - The package definition file contains headers followed by a data section.
// Headers are lines (≤ 4kb) that do not start with "$$".
// - The data section starts with "$$B\n" followed by export data followed
// by an end of section marker "\n$$\n". (The section start "$$\n" is no
// longer supported.)
// - The export data starts with a format byte ('u') followed by the <data> in
// the given format. (See ReadExportDataHeader for older formats.)
//
// Putting this together, the bytes in a GC-created archive files are expected
// to look like the following.
// See cmd/internal/archive for more details on ar file headers.
//
// | <!arch>\n | ar file signature
// | __.PKGDEF...size...\n | ar header for __.PKGDEF including size.
// | go object <...>\n | objabi header
// | <optional headers>\n | other headers such as build id
// | $$B\n | binary format marker
// | u<data>\n | unified export <data>
// | $$\n | end-of-section marker
// | [optional padding] | padding byte (0x0A) if size is odd
// | [ar file header] | other ar files
// | [ar file data] |
func ReadUnified(r *bufio.Reader) (data []byte, err error) {
// We historically guaranteed headers at the default buffer size (4096) work.
// This ensures we can use ReadSlice throughout.
const minBufferSize = 4096
r = bufio.NewReaderSize(r, minBufferSize)
size, err := FindPackageDefinition(r)
if err != nil {
return
}
n := size
objapi, headers, err := ReadObjectHeaders(r)
if err != nil {
return
}
n -= len(objapi)
for _, h := range headers {
n -= len(h)
}
hdrlen, err := ReadExportDataHeader(r)
if err != nil {
return
}
n -= hdrlen
// size also includes the end of section marker. Remove that many bytes from the end.
const marker = "\n$$\n"
n -= len(marker)
if n < 0 {
err = fmt.Errorf("invalid size (%d) in the archive file: %d bytes remain without section headers (recompile package)", size, n)
return
}
// Read n bytes from buf.
data = make([]byte, n)
_, err = io.ReadFull(r, data)
if err != nil {
return
}
// Check for marker at the end.
var suffix [len(marker)]byte
_, err = io.ReadFull(r, suffix[:])
if err != nil {
return
}
if s := string(suffix[:]); s != marker {
err = fmt.Errorf("read %q instead of end-of-section marker (%q)", s, marker)
return
}
return
}
// FindPackageDefinition positions the reader r at the beginning of a package
// definition file ("__.PKGDEF") within a GC-created archive by reading
// from it, and returns the size of the package definition file in the archive.
//
// The reader must be positioned at the start of the archive file before calling
// this function, and "__.PKGDEF" is assumed to be the first file in the archive.
//
// See cmd/internal/archive for details on the archive format.
func FindPackageDefinition(r *bufio.Reader) (size int, err error) {
// Uses ReadSlice to limit risk of malformed inputs.
// Read first line to make sure this is an object file.
line, err := r.ReadSlice('\n')
if err != nil {
err = fmt.Errorf("can't find export data (%v)", err)
return
}
// Is the first line an archive file signature?
if string(line) != "!<arch>\n" {
err = fmt.Errorf("not the start of an archive file (%q)", line)
return
}
// package export block should be first
size = readArchiveHeader(r, "__.PKGDEF")
if size <= 0 {
err = fmt.Errorf("not a package file")
return
}
return
}
// ReadObjectHeaders reads object headers from the reader. Object headers are
// lines that do not start with an end-of-section marker "$$". The first header
// is the objabi header. On success, the reader will be positioned at the beginning
// of the end-of-section marker.
//
// It returns an error if any header does not fit in r.Size() bytes.
func ReadObjectHeaders(r *bufio.Reader) (objapi string, headers []string, err error) {
// line is a temporary buffer for headers.
// Use bounded reads (ReadSlice, Peek) to limit risk of malformed inputs.
var line []byte
// objapi header should be the first line
if line, err = r.ReadSlice('\n'); err != nil {
err = fmt.Errorf("can't find export data (%v)", err)
return
}
objapi = string(line)
// objapi header begins with "go object ".
if !strings.HasPrefix(objapi, "go object ") {
err = fmt.Errorf("not a go object file: %s", objapi)
return
}
// process remaining object header lines
for {
// check for an end of section marker "$$"
line, err = r.Peek(2)
if err != nil {
return
}
if string(line) == "$$" {
return // stop
}
// read next header
line, err = r.ReadSlice('\n')
if err != nil {
return
}
headers = append(headers, string(line))
}
}
// ReadExportDataHeader reads the export data header and format from r.
// It returns the number of bytes read, or an error if the format is no longer
// supported or it failed to read.
//
// The only currently supported format is binary export data in the
// unified export format.
func ReadExportDataHeader(r *bufio.Reader) (n int, err error) {
// Read export data header.
line, err := r.ReadSlice('\n')
if err != nil {
return
}
hdr := string(line)
switch hdr {
case "$$\n":
err = fmt.Errorf("old textual export format no longer supported (recompile package)")
return
case "$$B\n":
var format byte
format, err = r.ReadByte()
if err != nil {
return
}
// The unified export format starts with a 'u'.
switch format {
case 'u':
default:
// Older no longer supported export formats include:
// indexed export format which started with an 'i'; and
// the older binary export format which started with a 'c',
// 'd', or 'v' (from "version").
err = fmt.Errorf("binary export format %q is no longer supported (recompile package)", format)
return
}
default:
err = fmt.Errorf("unknown export data header: %q", hdr)
return
}
n = len(hdr) + 1 // + 1 is for 'u'
return
}
// FindPkg returns the filename and unique package id for an import
// path based on package information provided by build.Import (using
// the build.Default build.Context). A relative srcDir is interpreted
// relative to the current working directory.
//
// FindPkg is only used in tests within x/tools.
func FindPkg(path, srcDir string) (filename, id string, err error) {
// TODO(taking): Move internal/exportdata.FindPkg into its own file,
// and then this copy into a _test package.
if path == "" {
return "", "", errors.New("path is empty")
}
var noext string
switch {
default:
// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
// Don't require the source files to be present.
if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282
srcDir = abs
}
var bp *build.Package
bp, err = build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
if bp.PkgObj == "" {
if bp.Goroot && bp.Dir != "" {
filename, err = lookupGorootExport(bp.Dir)
if err == nil {
_, err = os.Stat(filename)
}
if err == nil {
return filename, bp.ImportPath, nil
}
}
goto notfound
} else {
noext = strings.TrimSuffix(bp.PkgObj, ".a")
}
id = bp.ImportPath
case build.IsLocalImport(path):
// "./x" -> "/this/directory/x.ext", "/this/directory/x"
noext = filepath.Join(srcDir, path)
id = noext
case filepath.IsAbs(path):
// for completeness only - go/build.Import
// does not support absolute imports
// "/x" -> "/x.ext", "/x"
noext = path
id = path
}
if false { // for debugging
if path != id {
fmt.Printf("%s -> %s\n", path, id)
}
}
// try extensions
for _, ext := range pkgExts {
filename = noext + ext
f, statErr := os.Stat(filename)
if statErr == nil && !f.IsDir() {
return filename, id, nil
}
if err == nil {
err = statErr
}
}
notfound:
if err == nil {
return "", path, fmt.Errorf("can't find import: %q", path)
}
return "", path, fmt.Errorf("can't find import: %q: %w", path, err)
}
var pkgExts = [...]string{".a", ".o"} // a file from the build cache will have no extension
var exportMap sync.Map // package dir → func() (string, error)
// lookupGorootExport returns the location of the export data
// (normally found in the build cache, but located in GOROOT/pkg
// in prior Go releases) for the package located in pkgDir.
//
// (We use the package's directory instead of its import path
// mainly to simplify handling of the packages in src/vendor
// and cmd/vendor.)
//
// lookupGorootExport is only used in tests within x/tools.
func lookupGorootExport(pkgDir string) (string, error) {
f, ok := exportMap.Load(pkgDir)
if !ok {
var (
listOnce sync.Once
exportPath string
err error
)
f, _ = exportMap.LoadOrStore(pkgDir, func() (string, error) {
listOnce.Do(func() {
cmd := exec.Command(filepath.Join(build.Default.GOROOT, "bin", "go"), "list", "-export", "-f", "{{.Export}}", pkgDir)
cmd.Dir = build.Default.GOROOT
cmd.Env = append(os.Environ(), "PWD="+cmd.Dir, "GOROOT="+build.Default.GOROOT)
var output []byte
output, err = cmd.Output()
if err != nil {
if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
err = errors.New(string(ee.Stderr))
}
return
}
exports := strings.Split(string(bytes.TrimSpace(output)), "\n")
if len(exports) != 1 {
err = fmt.Errorf("go list reported %d exports; expected 1", len(exports))
return
}
exportPath = exports[0]
})
return exportPath, err
})
}
return f.(func() (string, error))()
}

View File

@@ -0,0 +1,108 @@
// Copyright 2011 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.
// This file is a reduced copy of $GOROOT/src/go/internal/gcimporter/gcimporter.go.
// Package gcimporter provides various functions for reading
// gc-generated object files that can be used to implement the
// Importer interface defined by the Go 1.5 standard library package.
//
// The encoding is deterministic: if the encoder is applied twice to
// the same types.Package data structure, both encodings are equal.
// This property may be important to avoid spurious changes in
// applications such as build systems.
//
// However, the encoder is not necessarily idempotent. Importing an
// exported package may yield a types.Package that, while it
// represents the same set of Go types as the original, may differ in
// the details of its internal representation. Because of these
// differences, re-encoding the imported package may yield a
// different, but equally valid, encoding of the package.
package gcimporter // import "golang.org/x/tools/internal/gcimporter"
import (
"bufio"
"fmt"
"go/token"
"go/types"
"io"
"os"
)
const (
// Enable debug during development: it adds some additional checks, and
// prevents errors from being recovered.
debug = false
// If trace is set, debugging output is printed to std out.
trace = false
)
// Import imports a gc-generated package given its import path and srcDir, adds
// the corresponding package object to the packages map, and returns the object.
// The packages map must contain all packages already imported.
//
// Import is only used in tests.
func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) {
var rc io.ReadCloser
var id string
if lookup != nil {
// With custom lookup specified, assume that caller has
// converted path to a canonical import path for use in the map.
if path == "unsafe" {
return types.Unsafe, nil
}
id = path
// No need to re-import if the package was imported completely before.
if pkg = packages[id]; pkg != nil && pkg.Complete() {
return
}
f, err := lookup(path)
if err != nil {
return nil, err
}
rc = f
} else {
var filename string
filename, id, err = FindPkg(path, srcDir)
if filename == "" {
if path == "unsafe" {
return types.Unsafe, nil
}
return nil, err
}
// no need to re-import if the package was imported completely before
if pkg = packages[id]; pkg != nil && pkg.Complete() {
return
}
// open file
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
// add file name to error
err = fmt.Errorf("%s: %v", filename, err)
}
}()
rc = f
}
defer rc.Close()
buf := bufio.NewReader(rc)
data, err := ReadUnified(buf)
if err != nil {
err = fmt.Errorf("import %q: %v", path, err)
return
}
// unified: emitted by cmd/compile since go1.20.
_, pkg, err = UImportData(fset, packages, data, id)
return
}

1603
vendor/golang.org/x/tools/internal/gcimporter/iexport.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

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