mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/vendor: update to x/tools@68724af
This causes the go1.26 vet printf analyzer to deduce printf wrappers via interface methods (#76368). For #76368 Change-Id: I80e6d3b21bdffb5d925a162af7c4b21b1357bb89 Reviewed-on: https://go-review.googlesource.com/c/go/+/722540 Auto-Submit: Alan Donovan <adonovan@google.com> Commit-Queue: Alan Donovan <adonovan@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
8c3195973b
commit
1bc54868d4
12 changed files with 1115 additions and 202 deletions
|
|
@ -11,7 +11,7 @@ require (
|
|||
golang.org/x/sys v0.38.0
|
||||
golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54
|
||||
golang.org/x/term v0.34.0
|
||||
golang.org/x/tools v0.39.1-0.20251114194111-59ff18ce4883
|
||||
golang.org/x/tools v0.39.1-0.20251120214200-68724afed209
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
|||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/tools v0.39.1-0.20251114194111-59ff18ce4883 h1:aeO0AW8d+a+5+hNQx9f4J5egD89zftrY2x42KGQjLzI=
|
||||
golang.org/x/tools v0.39.1-0.20251114194111-59ff18ce4883/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/tools v0.39.1-0.20251120214200-68724afed209 h1:BGuEUnbWU1H+VhF4Z52lwCvzRT8Q/Z7kJC3okSME58w=
|
||||
golang.org/x/tools v0.39.1-0.20251120214200-68724afed209/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef h1:mqLYrXCXYEZOop9/Dbo6RPX11539nwiCNBb1icVPmw8=
|
||||
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ=
|
||||
|
|
|
|||
49
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go
generated
vendored
49
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go
generated
vendored
|
|
@ -21,6 +21,7 @@ import (
|
|||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
"golang.org/x/tools/internal/analysis/analyzerutil"
|
||||
typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex"
|
||||
"golang.org/x/tools/internal/astutil"
|
||||
"golang.org/x/tools/internal/diff"
|
||||
"golang.org/x/tools/internal/moreiters"
|
||||
|
|
@ -28,6 +29,7 @@ import (
|
|||
"golang.org/x/tools/internal/refactor"
|
||||
"golang.org/x/tools/internal/refactor/inline"
|
||||
"golang.org/x/tools/internal/typesinternal"
|
||||
"golang.org/x/tools/internal/typesinternal/typeindex"
|
||||
)
|
||||
|
||||
//go:embed doc.go
|
||||
|
|
@ -43,20 +45,29 @@ var Analyzer = &analysis.Analyzer{
|
|||
(*goFixInlineConstFact)(nil),
|
||||
(*goFixInlineAliasFact)(nil),
|
||||
},
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Requires: []*analysis.Analyzer{
|
||||
inspect.Analyzer,
|
||||
typeindexanalyzer.Analyzer,
|
||||
},
|
||||
}
|
||||
|
||||
var allowBindingDecl bool
|
||||
var (
|
||||
allowBindingDecl bool
|
||||
lazyEdits bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
Analyzer.Flags.BoolVar(&allowBindingDecl, "allow_binding_decl", false,
|
||||
"permit inlinings that require a 'var params = args' declaration")
|
||||
Analyzer.Flags.BoolVar(&lazyEdits, "lazy_edits", false,
|
||||
"compute edits lazily (only meaningful to gopls driver)")
|
||||
}
|
||||
|
||||
// analyzer holds the state for this analysis.
|
||||
type analyzer struct {
|
||||
pass *analysis.Pass
|
||||
root inspector.Cursor
|
||||
index *typeindex.Index
|
||||
// memoization of repeated calls for same file.
|
||||
fileContent map[string][]byte
|
||||
// memoization of fact imports (nil => no fact)
|
||||
|
|
@ -69,6 +80,7 @@ func run(pass *analysis.Pass) (any, error) {
|
|||
a := &analyzer{
|
||||
pass: pass,
|
||||
root: pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Root(),
|
||||
index: pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index),
|
||||
fileContent: make(map[string][]byte),
|
||||
inlinableFuncs: make(map[*types.Func]*inline.Callee),
|
||||
inlinableConsts: make(map[*types.Const]*goFixInlineConstFact),
|
||||
|
|
@ -170,6 +182,27 @@ func (a *analyzer) inlineCall(call *ast.CallExpr, cur inspector.Cursor) {
|
|||
return // don't inline a function from within its own test
|
||||
}
|
||||
|
||||
// Compute the edits.
|
||||
//
|
||||
// Ordinarily the analyzer reports a fix containing
|
||||
// edits. However, the algorithm is somewhat expensive
|
||||
// (unnecessarily so: see go.dev/issue/75773) so
|
||||
// to reduce costs in gopls, we omit the edits,
|
||||
// meaning that gopls must compute them on demand
|
||||
// (based on the Diagnostic.Category) when they are
|
||||
// requested via a code action.
|
||||
//
|
||||
// This does mean that the following categories of
|
||||
// caller-dependent obstacles to inlining will be
|
||||
// reported when the gopls user requests the fix,
|
||||
// rather than by quietly suppressing the diagnostic:
|
||||
// - shadowing problems
|
||||
// - callee imports inaccessible "internal" packages
|
||||
// - callee refers to nonexported symbols
|
||||
// - callee uses too-new Go features
|
||||
// - inlining call from a cgo file
|
||||
var edits []analysis.TextEdit
|
||||
if !lazyEdits {
|
||||
// Inline the call.
|
||||
content, err := a.readFile(call)
|
||||
if err != nil {
|
||||
|
|
@ -184,6 +217,9 @@ func (a *analyzer) inlineCall(call *ast.CallExpr, cur inspector.Cursor) {
|
|||
File: curFile,
|
||||
Call: call,
|
||||
Content: content,
|
||||
CountUses: func(pkgname *types.PkgName) int {
|
||||
return moreiters.Len(a.index.Uses(pkgname))
|
||||
},
|
||||
}
|
||||
res, err := inline.Inline(caller, callee, &inline.Options{Logf: discard})
|
||||
if err != nil {
|
||||
|
|
@ -211,22 +247,23 @@ func (a *analyzer) inlineCall(call *ast.CallExpr, cur inspector.Cursor) {
|
|||
}
|
||||
got := res.Content
|
||||
|
||||
// Suggest the "fix".
|
||||
var textEdits []analysis.TextEdit
|
||||
for _, edit := range diff.Bytes(content, got) {
|
||||
textEdits = append(textEdits, analysis.TextEdit{
|
||||
edits = append(edits, analysis.TextEdit{
|
||||
Pos: curFile.FileStart + token.Pos(edit.Start),
|
||||
End: curFile.FileStart + token.Pos(edit.End),
|
||||
NewText: []byte(edit.New),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
a.pass.Report(analysis.Diagnostic{
|
||||
Pos: call.Pos(),
|
||||
End: call.End(),
|
||||
Message: fmt.Sprintf("Call of %v should be inlined", callee),
|
||||
Category: "inline_call", // keep consistent with gopls/internal/golang.fixInlineCall
|
||||
SuggestedFixes: []analysis.SuggestedFix{{
|
||||
Message: fmt.Sprintf("Inline call of %v", callee),
|
||||
TextEdits: textEdits,
|
||||
TextEdits: edits, // within gopls, this is nil => compute fix's edits lazily
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
11
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/maps.go
generated
vendored
11
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/maps.go
generated
vendored
|
|
@ -233,17 +233,20 @@ func mapsloop(pass *analysis.Pass) (any, error) {
|
|||
assign := rng.Body.List[0].(*ast.AssignStmt)
|
||||
if index, ok := assign.Lhs[0].(*ast.IndexExpr); ok &&
|
||||
astutil.EqualSyntax(rng.Key, index.Index) &&
|
||||
astutil.EqualSyntax(rng.Value, assign.Rhs[0]) &&
|
||||
is[*types.Map](typeparams.CoreType(info.TypeOf(index.X))) &&
|
||||
types.Identical(info.TypeOf(index), info.TypeOf(rng.Value)) { // m[k], v
|
||||
astutil.EqualSyntax(rng.Value, assign.Rhs[0]) {
|
||||
if tmap, ok := typeparams.CoreType(info.TypeOf(index.X)).(*types.Map); ok &&
|
||||
types.Identical(info.TypeOf(index), info.TypeOf(rng.Value)) && // m[k], v
|
||||
types.Identical(tmap.Key(), info.TypeOf(rng.Key)) {
|
||||
|
||||
// Have: for k, v := range x { m[k] = v }
|
||||
// where there is no implicit conversion.
|
||||
// where there is no implicit conversion
|
||||
// of either key or value.
|
||||
check(file, curRange, assign, index.X, rng.X)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
2
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go
generated
vendored
2
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go
generated
vendored
|
|
@ -489,7 +489,7 @@ func isNegativeConst(info *types.Info, expr ast.Expr) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// isNoneNegativeConst returns true if the expr is a const int with value >= zero.
|
||||
// isNonNegativeConst returns true if the expr is a const int with value >= zero.
|
||||
func isNonNegativeConst(info *types.Info, expr ast.Expr) bool {
|
||||
if tv, ok := info.Types[expr]; ok && tv.Value != nil && tv.Value.Kind() == constant.Int {
|
||||
if v, ok := constant.Int64Val(tv.Value); ok {
|
||||
|
|
|
|||
30
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/doc.go
generated
vendored
30
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/doc.go
generated
vendored
|
|
@ -92,6 +92,36 @@
|
|||
// }
|
||||
// logf("%s", 123) // logf format %s has arg 123 of wrong type int
|
||||
//
|
||||
// Interface methods may also be analyzed as printf wrappers, if
|
||||
// within the interface's package there is an assignment from a
|
||||
// implementation type whose corresponding method is a printf wrapper.
|
||||
//
|
||||
// For example, the var declaration below causes a *myLoggerImpl value
|
||||
// to be assigned to a Logger variable:
|
||||
//
|
||||
// type Logger interface {
|
||||
// Logf(format string, args ...any)
|
||||
// }
|
||||
//
|
||||
// type myLoggerImpl struct{ ... }
|
||||
//
|
||||
// var _ Logger = (*myLoggerImpl)(nil)
|
||||
//
|
||||
// func (*myLoggerImpl) Logf(format string, args ...any) {
|
||||
// println(fmt.Sprintf(format, args...))
|
||||
// }
|
||||
//
|
||||
// Since myLoggerImpl's Logf method is a printf wrapper, this
|
||||
// establishes that Logger.Logf is a printf wrapper too, causing
|
||||
// dynamic calls through the interface to be checked:
|
||||
//
|
||||
// func f(log Logger) {
|
||||
// log.Logf("%s", 123) // Logger.Logf format %s has arg 123 of wrong type int
|
||||
// }
|
||||
//
|
||||
// This feature applies only to interface methods declared in files
|
||||
// using at least Go 1.26.
|
||||
//
|
||||
// # Specifying printf wrappers by flag
|
||||
//
|
||||
// The -funcs flag specifies a comma-separated list of names of
|
||||
|
|
|
|||
257
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
generated
vendored
257
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
generated
vendored
|
|
@ -27,6 +27,7 @@ import (
|
|||
"golang.org/x/tools/internal/typeparams"
|
||||
"golang.org/x/tools/internal/typesinternal"
|
||||
"golang.org/x/tools/internal/versions"
|
||||
"golang.org/x/tools/refactor/satisfy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -65,7 +66,7 @@ func (kind Kind) String() string {
|
|||
case KindErrorf:
|
||||
return "errorf"
|
||||
}
|
||||
return ""
|
||||
return "(none)"
|
||||
}
|
||||
|
||||
// Result is the printf analyzer's result type. Clients may query the result
|
||||
|
|
@ -138,7 +139,7 @@ type wrapper struct {
|
|||
|
||||
type printfCaller struct {
|
||||
w *wrapper
|
||||
call *ast.CallExpr
|
||||
call *ast.CallExpr // forwarding call (nil for implicit interface method -> impl calls)
|
||||
}
|
||||
|
||||
// formatArgsParams returns the "format string" and "args ...any"
|
||||
|
|
@ -183,60 +184,12 @@ func findPrintLike(pass *analysis.Pass, res *Result) {
|
|||
wrappers []*wrapper
|
||||
byObj = make(map[types.Object]*wrapper)
|
||||
)
|
||||
for cur := range inspect.Root().Preorder((*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) {
|
||||
var (
|
||||
curBody inspector.Cursor // for *ast.BlockStmt
|
||||
sig *types.Signature
|
||||
obj types.Object
|
||||
)
|
||||
switch f := cur.Node().(type) {
|
||||
case *ast.FuncDecl:
|
||||
// named function or method:
|
||||
//
|
||||
// func wrapf(format string, args ...any) {...}
|
||||
if f.Body != nil {
|
||||
curBody = cur.ChildAt(edge.FuncDecl_Body, -1)
|
||||
obj = info.Defs[f.Name]
|
||||
sig = obj.Type().(*types.Signature)
|
||||
}
|
||||
for cur := range inspect.Root().Preorder((*ast.FuncDecl)(nil), (*ast.FuncLit)(nil), (*ast.InterfaceType)(nil)) {
|
||||
|
||||
case *ast.FuncLit:
|
||||
// anonymous function directly assigned to a variable:
|
||||
//
|
||||
// var wrapf = func(format string, args ...any) {...}
|
||||
// wrapf := func(format string, args ...any) {...}
|
||||
// wrapf = func(format string, args ...any) {...}
|
||||
//
|
||||
// The LHS may also be a struct field x.wrapf or
|
||||
// an imported var pkg.Wrapf.
|
||||
//
|
||||
sig = info.TypeOf(f).(*types.Signature)
|
||||
curBody = cur.ChildAt(edge.FuncLit_Body, -1)
|
||||
var lhs ast.Expr
|
||||
switch ek, idx := cur.ParentEdge(); ek {
|
||||
case edge.ValueSpec_Values:
|
||||
curName := cur.Parent().ChildAt(edge.ValueSpec_Names, idx)
|
||||
lhs = curName.Node().(*ast.Ident)
|
||||
case edge.AssignStmt_Rhs:
|
||||
curLhs := cur.Parent().ChildAt(edge.AssignStmt_Lhs, idx)
|
||||
lhs = curLhs.Node().(ast.Expr)
|
||||
}
|
||||
|
||||
switch lhs := lhs.(type) {
|
||||
case *ast.Ident:
|
||||
// variable: wrapf = func(...)
|
||||
obj = info.ObjectOf(lhs).(*types.Var)
|
||||
case *ast.SelectorExpr:
|
||||
if sel, ok := info.Selections[lhs]; ok {
|
||||
// struct field: x.wrapf = func(...)
|
||||
obj = sel.Obj().(*types.Var)
|
||||
} else {
|
||||
// imported var: pkg.Wrapf = func(...)
|
||||
obj = info.Uses[lhs.Sel].(*types.Var)
|
||||
}
|
||||
}
|
||||
}
|
||||
if obj != nil {
|
||||
// addWrapper records that a func (or var representing
|
||||
// a FuncLit) is a potential print{,f} wrapper.
|
||||
// curBody is its *ast.BlockStmt, if any.
|
||||
addWrapper := func(obj types.Object, sig *types.Signature, curBody inspector.Cursor) *wrapper {
|
||||
format, args := formatArgsParams(sig)
|
||||
if args != nil {
|
||||
// obj (the symbol for a function/method, or variable
|
||||
|
|
@ -254,17 +207,124 @@ func findPrintLike(pass *analysis.Pass, res *Result) {
|
|||
}
|
||||
byObj[w.obj] = w
|
||||
wrappers = append(wrappers, w)
|
||||
return w
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch f := cur.Node().(type) {
|
||||
case *ast.FuncDecl:
|
||||
// named function or method:
|
||||
//
|
||||
// func wrapf(format string, args ...any) {...}
|
||||
if f.Body != nil {
|
||||
fn := info.Defs[f.Name].(*types.Func)
|
||||
addWrapper(fn, fn.Signature(), cur.ChildAt(edge.FuncDecl_Body, -1))
|
||||
}
|
||||
|
||||
case *ast.FuncLit:
|
||||
// anonymous function directly assigned to a variable:
|
||||
//
|
||||
// var wrapf = func(format string, args ...any) {...}
|
||||
// wrapf := func(format string, args ...any) {...}
|
||||
// wrapf = func(format string, args ...any) {...}
|
||||
//
|
||||
// The LHS may also be a struct field x.wrapf or
|
||||
// an imported var pkg.Wrapf.
|
||||
//
|
||||
var lhs ast.Expr
|
||||
switch ek, idx := cur.ParentEdge(); ek {
|
||||
case edge.ValueSpec_Values:
|
||||
curName := cur.Parent().ChildAt(edge.ValueSpec_Names, idx)
|
||||
lhs = curName.Node().(*ast.Ident)
|
||||
case edge.AssignStmt_Rhs:
|
||||
curLhs := cur.Parent().ChildAt(edge.AssignStmt_Lhs, idx)
|
||||
lhs = curLhs.Node().(ast.Expr)
|
||||
}
|
||||
|
||||
var v *types.Var
|
||||
switch lhs := lhs.(type) {
|
||||
case *ast.Ident:
|
||||
// variable: wrapf = func(...)
|
||||
v = info.ObjectOf(lhs).(*types.Var)
|
||||
case *ast.SelectorExpr:
|
||||
if sel, ok := info.Selections[lhs]; ok {
|
||||
// struct field: x.wrapf = func(...)
|
||||
v = sel.Obj().(*types.Var)
|
||||
} else {
|
||||
// imported var: pkg.Wrapf = func(...)
|
||||
v = info.Uses[lhs.Sel].(*types.Var)
|
||||
}
|
||||
}
|
||||
if v != nil {
|
||||
sig := info.TypeOf(f).(*types.Signature)
|
||||
curBody := cur.ChildAt(edge.FuncLit_Body, -1)
|
||||
addWrapper(v, sig, curBody)
|
||||
}
|
||||
|
||||
case *ast.InterfaceType:
|
||||
// Induction through interface methods is gated as
|
||||
// if it were a go1.26 language feature, to avoid
|
||||
// surprises when go test's vet suite gets stricter.
|
||||
if analyzerutil.FileUsesGoVersion(pass, astutil.EnclosingFile(cur), versions.Go1_26) {
|
||||
for imeth := range info.TypeOf(f).(*types.Interface).Methods() {
|
||||
addWrapper(imeth, imeth.Signature(), inspector.Cursor{})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impls maps abstract methods to implementations.
|
||||
//
|
||||
// Interface methods are modelled as if they have a body
|
||||
// that calls each implementing method.
|
||||
//
|
||||
// In the code below, impls maps Logger.Logf to
|
||||
// [myLogger.Logf], and if myLogger.Logf is discovered to be
|
||||
// printf-like, then so will be Logger.Logf.
|
||||
//
|
||||
// type Logger interface {
|
||||
// Logf(format string, args ...any)
|
||||
// }
|
||||
// type myLogger struct{ ... }
|
||||
// func (myLogger) Logf(format string, args ...any) {...}
|
||||
// var _ Logger = myLogger{}
|
||||
impls := methodImplementations(pass)
|
||||
|
||||
// Pass 2: scan the body of each wrapper function
|
||||
// for calls to other printf-like functions.
|
||||
//
|
||||
// Also, reject tricky cases where the parameters
|
||||
// are potentially mutated by AssignStmt or UnaryExpr.
|
||||
// TODO: Relax these checks; issue 26555.
|
||||
for _, w := range wrappers {
|
||||
|
||||
// doCall records a call from one wrapper to another.
|
||||
doCall := func(callee types.Object, call *ast.CallExpr) {
|
||||
// Call from one wrapper candidate to another?
|
||||
// Record the edge so that if callee is found to be
|
||||
// a true wrapper, w will be too.
|
||||
if w2, ok := byObj[callee]; ok {
|
||||
w2.callers = append(w2.callers, printfCaller{w, call})
|
||||
}
|
||||
|
||||
// Is the candidate a true wrapper, because it calls
|
||||
// a known print{,f}-like function from the allowlist
|
||||
// or an imported fact, or another wrapper found
|
||||
// to be a true wrapper?
|
||||
// If so, convert all w's callers to kind.
|
||||
kind := callKind(pass, callee, res)
|
||||
if kind != KindNone {
|
||||
checkForward(pass, w, call, kind, res)
|
||||
}
|
||||
}
|
||||
|
||||
// An interface method has no body, but acts
|
||||
// like an implicit call to each implementing method.
|
||||
if w.curBody.Inspector() == nil {
|
||||
for impl := range impls[w.obj.(*types.Func)] {
|
||||
doCall(impl, nil)
|
||||
}
|
||||
continue // (no body)
|
||||
}
|
||||
|
||||
// Process all calls in the wrapper function's body.
|
||||
scan:
|
||||
for cur := range w.curBody.Preorder(
|
||||
(*ast.AssignStmt)(nil),
|
||||
|
|
@ -272,6 +332,12 @@ func findPrintLike(pass *analysis.Pass, res *Result) {
|
|||
(*ast.CallExpr)(nil),
|
||||
) {
|
||||
switch n := cur.Node().(type) {
|
||||
|
||||
// Reject tricky cases where the parameters
|
||||
// are potentially mutated by AssignStmt or UnaryExpr.
|
||||
// (This logic checks for mutation only before the call.)
|
||||
// TODO: Relax these checks; issue 26555.
|
||||
|
||||
case *ast.AssignStmt:
|
||||
// If the wrapper updates format or args
|
||||
// it is not a simple wrapper.
|
||||
|
|
@ -294,28 +360,53 @@ func findPrintLike(pass *analysis.Pass, res *Result) {
|
|||
case *ast.CallExpr:
|
||||
if len(n.Args) > 0 && match(info, n.Args[len(n.Args)-1], w.args) {
|
||||
if callee := typeutil.Callee(pass.TypesInfo, n); callee != nil {
|
||||
doCall(callee, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call from one wrapper candidate to another?
|
||||
// Record the edge so that if callee is found to be
|
||||
// a true wrapper, w will be too.
|
||||
if w2, ok := byObj[callee]; ok {
|
||||
w2.callers = append(w2.callers, printfCaller{w, n})
|
||||
// methodImplementations returns the mapping from interface methods
|
||||
// declared in this package to their corresponding implementing
|
||||
// methods (which may also be interface methods), according to the set
|
||||
// of assignments to interface types that appear within this package.
|
||||
func methodImplementations(pass *analysis.Pass) map[*types.Func]map[*types.Func]bool {
|
||||
impls := make(map[*types.Func]map[*types.Func]bool)
|
||||
|
||||
// To find interface/implementation relations,
|
||||
// we use the 'satisfy' pass, but proposal #70638
|
||||
// provides a better way.
|
||||
//
|
||||
// This pass over the syntax could be factored out as
|
||||
// a separate analysis pass if it is needed by other
|
||||
// analyzers.
|
||||
var f satisfy.Finder
|
||||
f.Find(pass.TypesInfo, pass.Files)
|
||||
for assign := range f.Result {
|
||||
// Have: LHS = RHS, where LHS is an interface type.
|
||||
for imeth := range assign.LHS.Underlying().(*types.Interface).Methods() {
|
||||
// Limit to interface methods of current package.
|
||||
if imeth.Pkg() != pass.Pkg {
|
||||
continue
|
||||
}
|
||||
|
||||
// Is the candidate a true wrapper, because it calls
|
||||
// a known print{,f}-like function from the allowlist
|
||||
// or an imported fact, or another wrapper found
|
||||
// to be a true wrapper?
|
||||
// If so, convert all w's callers to kind.
|
||||
kind := callKind(pass, callee, res)
|
||||
if kind != KindNone {
|
||||
checkForward(pass, w, n, kind, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, args := formatArgsParams(imeth.Signature()); args == nil {
|
||||
continue // not print{,f}-like
|
||||
}
|
||||
|
||||
// Add implementing method to the set.
|
||||
impl, _, _ := types.LookupFieldOrMethod(assign.RHS, false, pass.Pkg, imeth.Name()) // can't fail
|
||||
set, ok := impls[imeth]
|
||||
if !ok {
|
||||
set = make(map[*types.Func]bool)
|
||||
impls[imeth] = set
|
||||
}
|
||||
set[impl.(*types.Func)] = true
|
||||
}
|
||||
}
|
||||
return impls
|
||||
}
|
||||
|
||||
func match(info *types.Info, arg ast.Expr, param *types.Var) bool {
|
||||
|
|
@ -323,9 +414,16 @@ func match(info *types.Info, arg ast.Expr, param *types.Var) bool {
|
|||
return ok && info.ObjectOf(id) == param
|
||||
}
|
||||
|
||||
// checkForward checks that a forwarding wrapper is forwarding correctly.
|
||||
// It diagnoses writing fmt.Printf(format, args) instead of fmt.Printf(format, args...).
|
||||
// checkForward checks whether a forwarding wrapper is forwarding correctly.
|
||||
// If so, it propagates changes in wrapper kind information backwards
|
||||
// through through the wrapper.callers graph of forwarding calls.
|
||||
//
|
||||
// If not, it reports a diagnostic that the user wrote
|
||||
// fmt.Printf(format, args) instead of fmt.Printf(format, args...).
|
||||
func checkForward(pass *analysis.Pass, w *wrapper, call *ast.CallExpr, kind Kind, res *Result) {
|
||||
// Check correct call forwarding.
|
||||
// (Interface methods forward correctly by construction.)
|
||||
if call != nil {
|
||||
matched := kind == KindPrint ||
|
||||
kind != KindNone && len(call.Args) >= 2 && match(pass.TypesInfo, call.Args[len(call.Args)-2], w.format)
|
||||
if !matched {
|
||||
|
|
@ -354,6 +452,7 @@ func checkForward(pass *analysis.Pass, w *wrapper, call *ast.CallExpr, kind Kind
|
|||
pass.ReportRangef(call, "missing ... in args forwarded to %s-like function", desc)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If the candidate's print{,f} status becomes known,
|
||||
// propagate it back to all its so-far known callers.
|
||||
|
|
@ -444,8 +543,6 @@ var isPrint = stringSet{
|
|||
"(*testing.common).Logf": true,
|
||||
"(*testing.common).Skip": true,
|
||||
"(*testing.common).Skipf": true,
|
||||
// *testing.T and B are detected by induction, but testing.TB is
|
||||
// an interface and the inference can't follow dynamic calls.
|
||||
"(testing.TB).Error": true,
|
||||
"(testing.TB).Errorf": true,
|
||||
"(testing.TB).Fatal": true,
|
||||
|
|
|
|||
2
src/cmd/vendor/golang.org/x/tools/internal/astutil/util.go
generated
vendored
2
src/cmd/vendor/golang.org/x/tools/internal/astutil/util.go
generated
vendored
|
|
@ -64,7 +64,7 @@ func NodeContains(n ast.Node, rng Range) bool {
|
|||
return NodeRange(n).Contains(rng)
|
||||
}
|
||||
|
||||
// NodeContainPos reports whether the Pos/End range of node n encloses
|
||||
// NodeContainsPos reports whether the Pos/End range of node n encloses
|
||||
// the given pos.
|
||||
//
|
||||
// Like [NodeRange], it treats the range of an [ast.File] as the
|
||||
|
|
|
|||
8
src/cmd/vendor/golang.org/x/tools/internal/moreiters/iters.go
generated
vendored
8
src/cmd/vendor/golang.org/x/tools/internal/moreiters/iters.go
generated
vendored
|
|
@ -45,3 +45,11 @@ func Any[T any](seq iter.Seq[T], pred func(T) bool) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the sequence (by iterating).
|
||||
func Len[T any](seq iter.Seq[T]) (n int) {
|
||||
for range seq {
|
||||
n++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
50
src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/inline.go
generated
vendored
50
src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/inline.go
generated
vendored
|
|
@ -40,7 +40,11 @@ type Caller struct {
|
|||
Info *types.Info
|
||||
File *ast.File
|
||||
Call *ast.CallExpr
|
||||
Content []byte // source of file containing
|
||||
Content []byte // source of file containing (TODO(adonovan): see comment at Result.Content)
|
||||
|
||||
// CountUses is an optional optimized computation of
|
||||
// the number of times pkgname appears in Info.Uses.
|
||||
CountUses func(pkgname *types.PkgName) int
|
||||
|
||||
path []ast.Node // path from call to root of file syntax tree
|
||||
enclosingFunc *ast.FuncDecl // top-level function/method enclosing the call, if any
|
||||
|
|
@ -57,6 +61,18 @@ type Options struct {
|
|||
|
||||
// Result holds the result of code transformation.
|
||||
type Result struct {
|
||||
// TODO(adonovan): the only textual results that should be
|
||||
// needed are (1) an edit in the vicinity of the call (either
|
||||
// to the CallExpr or one of its ancestors), and optionally
|
||||
// (2) an edit to the import declaration.
|
||||
// Change the inliner API to return a list of edits,
|
||||
// and not to accept a Caller.Content, as it is only
|
||||
// temptation to use such algorithmically expensive
|
||||
// operations as reformatting the entire file, which is
|
||||
// a significant source of non-linear dynamic behavior;
|
||||
// see https://go.dev/issue/75773.
|
||||
// This will require a sequence of changes to the tests
|
||||
// and the inliner algorithm itself.
|
||||
Content []byte // formatted, transformed content of caller file
|
||||
Literalized bool // chosen strategy replaced callee() with func(){...}()
|
||||
BindingDecl bool // transformation added "var params = args" declaration
|
||||
|
|
@ -432,27 +448,19 @@ func newImportState(logf func(string, ...any), caller *Caller, callee *gobCallee
|
|||
importMap: make(map[string][]string),
|
||||
}
|
||||
|
||||
// Build an index of used-once PkgNames.
|
||||
type pkgNameUse struct {
|
||||
count int
|
||||
id *ast.Ident // an arbitrary use
|
||||
}
|
||||
pkgNameUses := make(map[*types.PkgName]pkgNameUse)
|
||||
for id, obj := range caller.Info.Uses {
|
||||
// Provide an inefficient default implementation of CountUses.
|
||||
// (Ideally clients amortize this for the entire package.)
|
||||
countUses := caller.CountUses
|
||||
if countUses == nil {
|
||||
uses := make(map[*types.PkgName]int)
|
||||
for _, obj := range caller.Info.Uses {
|
||||
if pkgname, ok := obj.(*types.PkgName); ok {
|
||||
u := pkgNameUses[pkgname]
|
||||
u.id = id
|
||||
u.count++
|
||||
pkgNameUses[pkgname] = u
|
||||
uses[pkgname]++
|
||||
}
|
||||
}
|
||||
// soleUse returns the ident that refers to pkgname, if there is exactly one.
|
||||
soleUse := func(pkgname *types.PkgName) *ast.Ident {
|
||||
u := pkgNameUses[pkgname]
|
||||
if u.count == 1 {
|
||||
return u.id
|
||||
countUses = func(pkgname *types.PkgName) int {
|
||||
return uses[pkgname]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, imp := range caller.File.Imports {
|
||||
|
|
@ -472,8 +480,10 @@ func newImportState(logf func(string, ...any), caller *Caller, callee *gobCallee
|
|||
// If that is the case, proactively check if any of the callee FreeObjs
|
||||
// need this import. Doing so eagerly simplifies the resulting logic.
|
||||
needed := true
|
||||
sel, ok := ast.Unparen(caller.Call.Fun).(*ast.SelectorExpr)
|
||||
if ok && soleUse(pkgName) == sel.X {
|
||||
if sel, ok := ast.Unparen(caller.Call.Fun).(*ast.SelectorExpr); ok &&
|
||||
is[*ast.Ident](sel.X) &&
|
||||
caller.Info.Uses[sel.X.(*ast.Ident)] == pkgName &&
|
||||
countUses(pkgName) == 1 {
|
||||
needed = false // no longer needed by caller
|
||||
// Check to see if any of the inlined free objects need this package.
|
||||
for _, obj := range callee.FreeObjs {
|
||||
|
|
|
|||
727
src/cmd/vendor/golang.org/x/tools/refactor/satisfy/find.go
generated
vendored
Normal file
727
src/cmd/vendor/golang.org/x/tools/refactor/satisfy/find.go
generated
vendored
Normal file
|
|
@ -0,0 +1,727 @@
|
|||
// 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 satisfy inspects the type-checked ASTs of Go packages and
|
||||
// reports the set of discovered type constraints of the form (lhs, rhs
|
||||
// Type) where lhs is a non-trivial interface, rhs satisfies this
|
||||
// interface, and this fact is necessary for the package to be
|
||||
// well-typed.
|
||||
//
|
||||
// THIS PACKAGE IS EXPERIMENTAL AND MAY CHANGE AT ANY TIME.
|
||||
//
|
||||
// It is provided only for the gopls tool. It requires well-typed inputs.
|
||||
package satisfy // import "golang.org/x/tools/refactor/satisfy"
|
||||
|
||||
// NOTES:
|
||||
//
|
||||
// We don't care about numeric conversions, so we don't descend into
|
||||
// types or constant expressions. This is unsound because
|
||||
// constant expressions can contain arbitrary statements, e.g.
|
||||
// const x = len([1]func(){func() {
|
||||
// ...
|
||||
// }})
|
||||
//
|
||||
// Assignability conversions are possible in the following places:
|
||||
// - in assignments y = x, y := x, var y = x.
|
||||
// - from call argument types to formal parameter types
|
||||
// - in append and delete calls
|
||||
// - from return operands to result parameter types
|
||||
// - in composite literal T{k:v}, from k and v to T's field/element/key type
|
||||
// - in map[key] from key to the map's key type
|
||||
// - in comparisons x==y and switch x { case y: }.
|
||||
// - in explicit conversions T(x)
|
||||
// - in sends ch <- x, from x to the channel element type
|
||||
// - in type assertions x.(T) and switch x.(type) { case T: }
|
||||
//
|
||||
// The results of this pass provide information equivalent to the
|
||||
// ssa.MakeInterface and ssa.ChangeInterface instructions.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
// A Constraint records the fact that the RHS type does and must
|
||||
// satisfy the LHS type, which is an interface.
|
||||
// The names are suggestive of an assignment statement LHS = RHS.
|
||||
//
|
||||
// The constraint is implicitly universally quantified over any type
|
||||
// parameters appearing within the two types.
|
||||
type Constraint struct {
|
||||
LHS, RHS types.Type
|
||||
}
|
||||
|
||||
// A Finder inspects the type-checked ASTs of Go packages and
|
||||
// accumulates the set of type constraints (x, y) such that x is
|
||||
// assignable to y, y is an interface, and both x and y have methods.
|
||||
//
|
||||
// In other words, it returns the subset of the "implements" relation
|
||||
// that is checked during compilation of a package. Refactoring tools
|
||||
// will need to preserve at least this part of the relation to ensure
|
||||
// continued compilation.
|
||||
type Finder struct {
|
||||
Result map[Constraint]bool
|
||||
msetcache typeutil.MethodSetCache
|
||||
|
||||
// per-Find state
|
||||
info *types.Info
|
||||
sig *types.Signature
|
||||
}
|
||||
|
||||
// Find inspects a single package, populating Result with its pairs of
|
||||
// constrained types.
|
||||
//
|
||||
// The result is non-canonical and thus may contain duplicates (but this
|
||||
// tends to preserves names of interface types better).
|
||||
//
|
||||
// The package must be free of type errors, and
|
||||
// info.{Defs,Uses,Selections,Types} must have been populated by the
|
||||
// type-checker.
|
||||
func (f *Finder) Find(info *types.Info, files []*ast.File) {
|
||||
if info.Defs == nil || info.Uses == nil || info.Selections == nil || info.Types == nil {
|
||||
panic("Finder.Find: one of info.{Defs,Uses,Selections.Types} is not populated")
|
||||
}
|
||||
if f.Result == nil {
|
||||
f.Result = make(map[Constraint]bool)
|
||||
}
|
||||
|
||||
f.info = info
|
||||
for _, file := range files {
|
||||
for _, d := range file.Decls {
|
||||
switch d := d.(type) {
|
||||
case *ast.GenDecl:
|
||||
if d.Tok == token.VAR { // ignore consts
|
||||
for _, spec := range d.Specs {
|
||||
f.valueSpec(spec.(*ast.ValueSpec))
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.FuncDecl:
|
||||
if d.Body != nil {
|
||||
f.sig = f.info.Defs[d.Name].Type().(*types.Signature)
|
||||
f.stmt(d.Body)
|
||||
f.sig = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
f.info = nil
|
||||
}
|
||||
|
||||
var (
|
||||
tInvalid = types.Typ[types.Invalid]
|
||||
tUntypedBool = types.Typ[types.UntypedBool]
|
||||
tUntypedNil = types.Typ[types.UntypedNil]
|
||||
)
|
||||
|
||||
// exprN visits an expression in a multi-value context.
|
||||
func (f *Finder) exprN(e ast.Expr) types.Type {
|
||||
typ := f.info.Types[e].Type.(*types.Tuple)
|
||||
switch e := e.(type) {
|
||||
case *ast.ParenExpr:
|
||||
return f.exprN(e.X)
|
||||
|
||||
case *ast.CallExpr:
|
||||
// x, err := f(args)
|
||||
sig := typeparams.CoreType(f.expr(e.Fun)).(*types.Signature)
|
||||
f.call(sig, e.Args)
|
||||
|
||||
case *ast.IndexExpr:
|
||||
// y, ok := x[i]
|
||||
x := f.expr(e.X)
|
||||
f.assign(f.expr(e.Index), typeparams.CoreType(x).(*types.Map).Key())
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
// y, ok := x.(T)
|
||||
f.typeAssert(f.expr(e.X), typ.At(0).Type())
|
||||
|
||||
case *ast.UnaryExpr: // must be receive <-
|
||||
// y, ok := <-x
|
||||
f.expr(e.X)
|
||||
|
||||
default:
|
||||
panic(e)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
func (f *Finder) call(sig *types.Signature, args []ast.Expr) {
|
||||
if len(args) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Ellipsis call? e.g. f(x, y, z...)
|
||||
if _, ok := args[len(args)-1].(*ast.Ellipsis); ok {
|
||||
for i, arg := range args {
|
||||
// The final arg is a slice, and so is the final param.
|
||||
f.assign(sig.Params().At(i).Type(), f.expr(arg))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var argtypes []types.Type
|
||||
|
||||
// Gather the effective actual parameter types.
|
||||
if tuple, ok := f.info.Types[args[0]].Type.(*types.Tuple); ok {
|
||||
// f(g()) call where g has multiple results?
|
||||
f.expr(args[0])
|
||||
// unpack the tuple
|
||||
for v := range tuple.Variables() {
|
||||
argtypes = append(argtypes, v.Type())
|
||||
}
|
||||
} else {
|
||||
for _, arg := range args {
|
||||
argtypes = append(argtypes, f.expr(arg))
|
||||
}
|
||||
}
|
||||
|
||||
// Assign the actuals to the formals.
|
||||
if !sig.Variadic() {
|
||||
for i, argtype := range argtypes {
|
||||
f.assign(sig.Params().At(i).Type(), argtype)
|
||||
}
|
||||
} else {
|
||||
// The first n-1 parameters are assigned normally.
|
||||
nnormals := sig.Params().Len() - 1
|
||||
for i, argtype := range argtypes[:nnormals] {
|
||||
f.assign(sig.Params().At(i).Type(), argtype)
|
||||
}
|
||||
// Remaining args are assigned to elements of varargs slice.
|
||||
tElem := sig.Params().At(nnormals).Type().(*types.Slice).Elem()
|
||||
for i := nnormals; i < len(argtypes); i++ {
|
||||
f.assign(tElem, argtypes[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// builtin visits the arguments of a builtin type with signature sig.
|
||||
func (f *Finder) builtin(obj *types.Builtin, sig *types.Signature, args []ast.Expr) {
|
||||
switch obj.Name() {
|
||||
case "make", "new":
|
||||
for i, arg := range args {
|
||||
if i == 0 && f.info.Types[arg].IsType() {
|
||||
continue // skip the type operand
|
||||
}
|
||||
f.expr(arg)
|
||||
}
|
||||
|
||||
case "append":
|
||||
s := f.expr(args[0])
|
||||
if _, ok := args[len(args)-1].(*ast.Ellipsis); ok && len(args) == 2 {
|
||||
// append(x, y...) including append([]byte, "foo"...)
|
||||
f.expr(args[1])
|
||||
} else {
|
||||
// append(x, y, z)
|
||||
tElem := typeparams.CoreType(s).(*types.Slice).Elem()
|
||||
for _, arg := range args[1:] {
|
||||
f.assign(tElem, f.expr(arg))
|
||||
}
|
||||
}
|
||||
|
||||
case "delete":
|
||||
m := f.expr(args[0])
|
||||
k := f.expr(args[1])
|
||||
f.assign(typeparams.CoreType(m).(*types.Map).Key(), k)
|
||||
|
||||
default:
|
||||
// ordinary call
|
||||
f.call(sig, args)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Finder) extract(tuple types.Type, i int) types.Type {
|
||||
if tuple, ok := tuple.(*types.Tuple); ok && i < tuple.Len() {
|
||||
return tuple.At(i).Type()
|
||||
}
|
||||
return tInvalid
|
||||
}
|
||||
|
||||
func (f *Finder) valueSpec(spec *ast.ValueSpec) {
|
||||
var T types.Type
|
||||
if spec.Type != nil {
|
||||
T = f.info.Types[spec.Type].Type
|
||||
}
|
||||
switch len(spec.Values) {
|
||||
case len(spec.Names): // e.g. var x, y = f(), g()
|
||||
for _, value := range spec.Values {
|
||||
v := f.expr(value)
|
||||
if T != nil {
|
||||
f.assign(T, v)
|
||||
}
|
||||
}
|
||||
|
||||
case 1: // e.g. var x, y = f()
|
||||
tuple := f.exprN(spec.Values[0])
|
||||
for i := range spec.Names {
|
||||
if T != nil {
|
||||
f.assign(T, f.extract(tuple, i))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// assign records pairs of distinct types that are related by
|
||||
// assignability, where the left-hand side is an interface and both
|
||||
// sides have methods.
|
||||
//
|
||||
// It should be called for all assignability checks, type assertions,
|
||||
// explicit conversions and comparisons between two types, unless the
|
||||
// types are uninteresting (e.g. lhs is a concrete type, or the empty
|
||||
// interface; rhs has no methods).
|
||||
func (f *Finder) assign(lhs, rhs types.Type) {
|
||||
if types.Identical(lhs, rhs) {
|
||||
return
|
||||
}
|
||||
if !types.IsInterface(lhs) {
|
||||
return
|
||||
}
|
||||
|
||||
if f.msetcache.MethodSet(lhs).Len() == 0 {
|
||||
return
|
||||
}
|
||||
if f.msetcache.MethodSet(rhs).Len() == 0 {
|
||||
return
|
||||
}
|
||||
// record the pair
|
||||
f.Result[Constraint{lhs, rhs}] = true
|
||||
}
|
||||
|
||||
// typeAssert must be called for each type assertion x.(T) where x has
|
||||
// interface type I.
|
||||
func (f *Finder) typeAssert(I, T types.Type) {
|
||||
// Type assertions are slightly subtle, because they are allowed
|
||||
// to be "impossible", e.g.
|
||||
//
|
||||
// var x interface{f()}
|
||||
// _ = x.(interface{f()int}) // legal
|
||||
//
|
||||
// (In hindsight, the language spec should probably not have
|
||||
// allowed this, but it's too late to fix now.)
|
||||
//
|
||||
// This means that a type assert from I to T isn't exactly a
|
||||
// constraint that T is assignable to I, but for a refactoring
|
||||
// tool it is a conditional constraint that, if T is assignable
|
||||
// to I before a refactoring, it should remain so after.
|
||||
|
||||
if types.AssignableTo(T, I) {
|
||||
f.assign(I, T)
|
||||
}
|
||||
}
|
||||
|
||||
// compare must be called for each comparison x==y.
|
||||
func (f *Finder) compare(x, y types.Type) {
|
||||
if types.AssignableTo(x, y) {
|
||||
f.assign(y, x)
|
||||
} else if types.AssignableTo(y, x) {
|
||||
f.assign(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
// expr visits a true expression (not a type or defining ident)
|
||||
// and returns its type.
|
||||
func (f *Finder) expr(e ast.Expr) types.Type {
|
||||
tv := f.info.Types[e]
|
||||
if tv.Value != nil {
|
||||
return tv.Type // prune the descent for constants
|
||||
}
|
||||
|
||||
// tv.Type may be nil for an ast.Ident.
|
||||
|
||||
switch e := e.(type) {
|
||||
case *ast.BadExpr, *ast.BasicLit:
|
||||
// no-op
|
||||
|
||||
case *ast.Ident:
|
||||
// (referring idents only)
|
||||
if obj, ok := f.info.Uses[e]; ok {
|
||||
return obj.Type()
|
||||
}
|
||||
if e.Name == "_" { // e.g. "for _ = range x"
|
||||
return tInvalid
|
||||
}
|
||||
panic("undefined ident: " + e.Name)
|
||||
|
||||
case *ast.Ellipsis:
|
||||
if e.Elt != nil {
|
||||
f.expr(e.Elt)
|
||||
}
|
||||
|
||||
case *ast.FuncLit:
|
||||
saved := f.sig
|
||||
f.sig = tv.Type.(*types.Signature)
|
||||
f.stmt(e.Body)
|
||||
f.sig = saved
|
||||
|
||||
case *ast.CompositeLit:
|
||||
switch T := typeparams.CoreType(typeparams.Deref(tv.Type)).(type) {
|
||||
case *types.Struct:
|
||||
for i, elem := range e.Elts {
|
||||
if kv, ok := elem.(*ast.KeyValueExpr); ok {
|
||||
f.assign(f.info.Uses[kv.Key.(*ast.Ident)].Type(), f.expr(kv.Value))
|
||||
} else {
|
||||
f.assign(T.Field(i).Type(), f.expr(elem))
|
||||
}
|
||||
}
|
||||
|
||||
case *types.Map:
|
||||
for _, elem := range e.Elts {
|
||||
elem := elem.(*ast.KeyValueExpr)
|
||||
f.assign(T.Key(), f.expr(elem.Key))
|
||||
f.assign(T.Elem(), f.expr(elem.Value))
|
||||
}
|
||||
|
||||
case *types.Array, *types.Slice:
|
||||
tElem := T.(interface {
|
||||
Elem() types.Type
|
||||
}).Elem()
|
||||
for _, elem := range e.Elts {
|
||||
if kv, ok := elem.(*ast.KeyValueExpr); ok {
|
||||
// ignore the key
|
||||
f.assign(tElem, f.expr(kv.Value))
|
||||
} else {
|
||||
f.assign(tElem, f.expr(elem))
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected composite literal type %T: %v", tv.Type, tv.Type.String()))
|
||||
}
|
||||
|
||||
case *ast.ParenExpr:
|
||||
f.expr(e.X)
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
if _, ok := f.info.Selections[e]; ok {
|
||||
f.expr(e.X) // selection
|
||||
} else {
|
||||
return f.info.Uses[e.Sel].Type() // qualified identifier
|
||||
}
|
||||
|
||||
case *ast.IndexExpr:
|
||||
if instance(f.info, e.X) {
|
||||
// f[T] or C[T] -- generic instantiation
|
||||
} else {
|
||||
// x[i] or m[k] -- index or lookup operation
|
||||
x := f.expr(e.X)
|
||||
i := f.expr(e.Index)
|
||||
if ux, ok := typeparams.CoreType(x).(*types.Map); ok {
|
||||
f.assign(ux.Key(), i)
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.IndexListExpr:
|
||||
// f[X, Y] -- generic instantiation
|
||||
|
||||
case *ast.SliceExpr:
|
||||
f.expr(e.X)
|
||||
if e.Low != nil {
|
||||
f.expr(e.Low)
|
||||
}
|
||||
if e.High != nil {
|
||||
f.expr(e.High)
|
||||
}
|
||||
if e.Max != nil {
|
||||
f.expr(e.Max)
|
||||
}
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
x := f.expr(e.X)
|
||||
f.typeAssert(x, f.info.Types[e.Type].Type)
|
||||
|
||||
case *ast.CallExpr:
|
||||
if tvFun := f.info.Types[e.Fun]; tvFun.IsType() {
|
||||
// conversion
|
||||
arg0 := f.expr(e.Args[0])
|
||||
f.assign(tvFun.Type, arg0)
|
||||
} else {
|
||||
// function call
|
||||
|
||||
// unsafe call. Treat calls to functions in unsafe like ordinary calls,
|
||||
// except that their signature cannot be determined by their func obj.
|
||||
// Without this special handling, f.expr(e.Fun) would fail below.
|
||||
if s, ok := ast.Unparen(e.Fun).(*ast.SelectorExpr); ok {
|
||||
if obj, ok := f.info.Uses[s.Sel].(*types.Builtin); ok && obj.Pkg().Path() == "unsafe" {
|
||||
sig := f.info.Types[e.Fun].Type.(*types.Signature)
|
||||
f.call(sig, e.Args)
|
||||
return tv.Type
|
||||
}
|
||||
}
|
||||
|
||||
// builtin call
|
||||
if id, ok := ast.Unparen(e.Fun).(*ast.Ident); ok {
|
||||
if obj, ok := f.info.Uses[id].(*types.Builtin); ok {
|
||||
sig := f.info.Types[id].Type.(*types.Signature)
|
||||
f.builtin(obj, sig, e.Args)
|
||||
return tv.Type
|
||||
}
|
||||
}
|
||||
|
||||
// ordinary call
|
||||
f.call(typeparams.CoreType(f.expr(e.Fun)).(*types.Signature), e.Args)
|
||||
}
|
||||
|
||||
case *ast.StarExpr:
|
||||
f.expr(e.X)
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
f.expr(e.X)
|
||||
|
||||
case *ast.BinaryExpr:
|
||||
x := f.expr(e.X)
|
||||
y := f.expr(e.Y)
|
||||
if e.Op == token.EQL || e.Op == token.NEQ {
|
||||
f.compare(x, y)
|
||||
}
|
||||
|
||||
case *ast.KeyValueExpr:
|
||||
f.expr(e.Key)
|
||||
f.expr(e.Value)
|
||||
|
||||
case *ast.ArrayType,
|
||||
*ast.StructType,
|
||||
*ast.FuncType,
|
||||
*ast.InterfaceType,
|
||||
*ast.MapType,
|
||||
*ast.ChanType:
|
||||
panic(e)
|
||||
}
|
||||
|
||||
if tv.Type == nil {
|
||||
panic(fmt.Sprintf("no type for %T", e))
|
||||
}
|
||||
|
||||
return tv.Type
|
||||
}
|
||||
|
||||
func (f *Finder) stmt(s ast.Stmt) {
|
||||
switch s := s.(type) {
|
||||
case *ast.BadStmt,
|
||||
*ast.EmptyStmt,
|
||||
*ast.BranchStmt:
|
||||
// no-op
|
||||
|
||||
case *ast.DeclStmt:
|
||||
d := s.Decl.(*ast.GenDecl)
|
||||
if d.Tok == token.VAR { // ignore consts
|
||||
for _, spec := range d.Specs {
|
||||
f.valueSpec(spec.(*ast.ValueSpec))
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.LabeledStmt:
|
||||
f.stmt(s.Stmt)
|
||||
|
||||
case *ast.ExprStmt:
|
||||
f.expr(s.X)
|
||||
|
||||
case *ast.SendStmt:
|
||||
ch := f.expr(s.Chan)
|
||||
val := f.expr(s.Value)
|
||||
f.assign(typeparams.CoreType(ch).(*types.Chan).Elem(), val)
|
||||
|
||||
case *ast.IncDecStmt:
|
||||
f.expr(s.X)
|
||||
|
||||
case *ast.AssignStmt:
|
||||
switch s.Tok {
|
||||
case token.ASSIGN, token.DEFINE:
|
||||
// y := x or y = x
|
||||
var rhsTuple types.Type
|
||||
if len(s.Lhs) != len(s.Rhs) {
|
||||
rhsTuple = f.exprN(s.Rhs[0])
|
||||
}
|
||||
for i := range s.Lhs {
|
||||
var lhs, rhs types.Type
|
||||
if rhsTuple == nil {
|
||||
rhs = f.expr(s.Rhs[i]) // 1:1 assignment
|
||||
} else {
|
||||
rhs = f.extract(rhsTuple, i) // n:1 assignment
|
||||
}
|
||||
|
||||
if id, ok := s.Lhs[i].(*ast.Ident); ok {
|
||||
if id.Name != "_" {
|
||||
if obj, ok := f.info.Defs[id]; ok {
|
||||
lhs = obj.Type() // definition
|
||||
}
|
||||
}
|
||||
}
|
||||
if lhs == nil {
|
||||
lhs = f.expr(s.Lhs[i]) // assignment
|
||||
}
|
||||
f.assign(lhs, rhs)
|
||||
}
|
||||
|
||||
default:
|
||||
// y op= x
|
||||
f.expr(s.Lhs[0])
|
||||
f.expr(s.Rhs[0])
|
||||
}
|
||||
|
||||
case *ast.GoStmt:
|
||||
f.expr(s.Call)
|
||||
|
||||
case *ast.DeferStmt:
|
||||
f.expr(s.Call)
|
||||
|
||||
case *ast.ReturnStmt:
|
||||
formals := f.sig.Results()
|
||||
switch len(s.Results) {
|
||||
case formals.Len(): // 1:1
|
||||
for i, result := range s.Results {
|
||||
f.assign(formals.At(i).Type(), f.expr(result))
|
||||
}
|
||||
|
||||
case 1: // n:1
|
||||
tuple := f.exprN(s.Results[0])
|
||||
for i := 0; i < formals.Len(); i++ {
|
||||
f.assign(formals.At(i).Type(), f.extract(tuple, i))
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.SelectStmt:
|
||||
f.stmt(s.Body)
|
||||
|
||||
case *ast.BlockStmt:
|
||||
for _, s := range s.List {
|
||||
f.stmt(s)
|
||||
}
|
||||
|
||||
case *ast.IfStmt:
|
||||
if s.Init != nil {
|
||||
f.stmt(s.Init)
|
||||
}
|
||||
f.expr(s.Cond)
|
||||
f.stmt(s.Body)
|
||||
if s.Else != nil {
|
||||
f.stmt(s.Else)
|
||||
}
|
||||
|
||||
case *ast.SwitchStmt:
|
||||
if s.Init != nil {
|
||||
f.stmt(s.Init)
|
||||
}
|
||||
var tag types.Type = tUntypedBool
|
||||
if s.Tag != nil {
|
||||
tag = f.expr(s.Tag)
|
||||
}
|
||||
for _, cc := range s.Body.List {
|
||||
cc := cc.(*ast.CaseClause)
|
||||
for _, cond := range cc.List {
|
||||
f.compare(tag, f.info.Types[cond].Type)
|
||||
}
|
||||
for _, s := range cc.Body {
|
||||
f.stmt(s)
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.TypeSwitchStmt:
|
||||
if s.Init != nil {
|
||||
f.stmt(s.Init)
|
||||
}
|
||||
var I types.Type
|
||||
switch ass := s.Assign.(type) {
|
||||
case *ast.ExprStmt: // x.(type)
|
||||
I = f.expr(ast.Unparen(ass.X).(*ast.TypeAssertExpr).X)
|
||||
case *ast.AssignStmt: // y := x.(type)
|
||||
I = f.expr(ast.Unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
|
||||
}
|
||||
for _, cc := range s.Body.List {
|
||||
cc := cc.(*ast.CaseClause)
|
||||
for _, cond := range cc.List {
|
||||
tCase := f.info.Types[cond].Type
|
||||
if tCase != tUntypedNil {
|
||||
f.typeAssert(I, tCase)
|
||||
}
|
||||
}
|
||||
for _, s := range cc.Body {
|
||||
f.stmt(s)
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.CommClause:
|
||||
if s.Comm != nil {
|
||||
f.stmt(s.Comm)
|
||||
}
|
||||
for _, s := range s.Body {
|
||||
f.stmt(s)
|
||||
}
|
||||
|
||||
case *ast.ForStmt:
|
||||
if s.Init != nil {
|
||||
f.stmt(s.Init)
|
||||
}
|
||||
if s.Cond != nil {
|
||||
f.expr(s.Cond)
|
||||
}
|
||||
if s.Post != nil {
|
||||
f.stmt(s.Post)
|
||||
}
|
||||
f.stmt(s.Body)
|
||||
|
||||
case *ast.RangeStmt:
|
||||
x := f.expr(s.X)
|
||||
// No conversions are involved when Tok==DEFINE.
|
||||
if s.Tok == token.ASSIGN {
|
||||
if s.Key != nil {
|
||||
k := f.expr(s.Key)
|
||||
var xelem types.Type
|
||||
// Keys of array, *array, slice, string aren't interesting
|
||||
// since the RHS key type is just an int.
|
||||
switch ux := typeparams.CoreType(x).(type) {
|
||||
case *types.Chan:
|
||||
xelem = ux.Elem()
|
||||
case *types.Map:
|
||||
xelem = ux.Key()
|
||||
}
|
||||
if xelem != nil {
|
||||
f.assign(k, xelem)
|
||||
}
|
||||
}
|
||||
if s.Value != nil {
|
||||
val := f.expr(s.Value)
|
||||
var xelem types.Type
|
||||
// Values of type strings aren't interesting because
|
||||
// the RHS value type is just a rune.
|
||||
switch ux := typeparams.CoreType(x).(type) {
|
||||
case *types.Array:
|
||||
xelem = ux.Elem()
|
||||
case *types.Map:
|
||||
xelem = ux.Elem()
|
||||
case *types.Pointer: // *array
|
||||
xelem = typeparams.CoreType(typeparams.Deref(ux)).(*types.Array).Elem()
|
||||
case *types.Slice:
|
||||
xelem = ux.Elem()
|
||||
}
|
||||
if xelem != nil {
|
||||
f.assign(val, xelem)
|
||||
}
|
||||
}
|
||||
}
|
||||
f.stmt(s.Body)
|
||||
|
||||
default:
|
||||
panic(s)
|
||||
}
|
||||
}
|
||||
|
||||
// -- Plundered from golang.org/x/tools/go/ssa -----------------
|
||||
|
||||
func instance(info *types.Info, expr ast.Expr) bool {
|
||||
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
|
||||
}
|
||||
3
src/cmd/vendor/modules.txt
vendored
3
src/cmd/vendor/modules.txt
vendored
|
|
@ -73,7 +73,7 @@ golang.org/x/text/internal/tag
|
|||
golang.org/x/text/language
|
||||
golang.org/x/text/transform
|
||||
golang.org/x/text/unicode/norm
|
||||
# golang.org/x/tools v0.39.1-0.20251114194111-59ff18ce4883
|
||||
# golang.org/x/tools v0.39.1-0.20251120214200-68724afed209
|
||||
## explicit; go 1.24.0
|
||||
golang.org/x/tools/cmd/bisect
|
||||
golang.org/x/tools/cover
|
||||
|
|
@ -149,6 +149,7 @@ golang.org/x/tools/internal/typeparams
|
|||
golang.org/x/tools/internal/typesinternal
|
||||
golang.org/x/tools/internal/typesinternal/typeindex
|
||||
golang.org/x/tools/internal/versions
|
||||
golang.org/x/tools/refactor/satisfy
|
||||
# rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef
|
||||
## explicit; go 1.20
|
||||
rsc.io/markdown
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue