mirror of
https://github.com/golang/go.git
synced 2025-12-28 06:34:04 +00:00
all: update vendored x/tools
Pull in the following x/tools changes: - CL 726000: go/analysis/passes/modernize: omitzero: suppress on kubebuilder - CL 726621: internal/refactor/inline: built-ins may affect inference - CL 727041: go/analysis/passes/modernize: fix stringscut false positives - CL 727040: go/analysis/unitchecker: write fixed files to an archive Fixes #76649. Fixes #76287. Fixes #76687. For #71859. [git-generate] go install golang.org/x/build/cmd/updatestd@latest go install golang.org/x/tools/cmd/bundle@latest updatestd -goroot=$(pwd) -branch=internal-branch.go1.26-vendor Change-Id: I0a369ad85b06adab3a977c2c523b8214fb53271a Reviewed-on: https://go-review.googlesource.com/c/go/+/727022 Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
a753a9ed54
commit
91267f0a70
@ -11,7 +11,7 @@ require (
|
||||
golang.org/x/sys v0.38.1-0.20251125153526-08e54827f670
|
||||
golang.org/x/telemetry v0.0.0-20251128220624-abf20d0e57ec
|
||||
golang.org/x/term v0.37.0
|
||||
golang.org/x/tools v0.39.1-0.20251130212600-1ad6f3d02713
|
||||
golang.org/x/tools v0.39.1-0.20251205000126-062ef7b6ced2
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@ -22,7 +22,7 @@ golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/text v0.31.1-0.20251128220601-087616b6cde9 h1:IjQf87/qLz2y0SiCc0uY3DwajALXkAgP1Pxal0mmdrM=
|
||||
golang.org/x/text v0.31.1-0.20251128220601-087616b6cde9/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/tools v0.39.1-0.20251130212600-1ad6f3d02713 h1:i4GzAuZW4RuKXltwKyLYAfk7E1TSKQBxRAI7XKfLjSk=
|
||||
golang.org/x/tools v0.39.1-0.20251130212600-1ad6f3d02713/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/tools v0.39.1-0.20251205000126-062ef7b6ced2 h1:2Qqv605Nus9iUp3ErvEU/q92Q3HAzeROztzl9pzAno8=
|
||||
golang.org/x/tools v0.39.1-0.20251205000126-062ef7b6ced2/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=
|
||||
|
||||
13
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go
generated
vendored
13
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go
generated
vendored
@ -199,12 +199,19 @@ are often used to express optionality.
|
||||
|
||||
omitzero: suggest replacing omitempty with omitzero for struct fields
|
||||
|
||||
The omitzero analyzer identifies uses of the `omitempty` JSON struct tag on
|
||||
fields that are themselves structs. The `omitempty` tag has no effect on
|
||||
struct-typed fields. The analyzer offers two suggestions: either remove the
|
||||
The omitzero analyzer identifies uses of the `omitempty` JSON struct
|
||||
tag on fields that are themselves structs. For struct-typed fields,
|
||||
the `omitempty` tag has no effect on the behavior of json.Marshal and
|
||||
json.Unmarshal. The analyzer offers two suggestions: either remove the
|
||||
tag, or replace it with `omitzero` (added in Go 1.24), which correctly
|
||||
omits the field if the struct value is zero.
|
||||
|
||||
However, some other serialization packages (notably kubebuilder, see
|
||||
https://book.kubebuilder.io/reference/markers.html) may have their own
|
||||
interpretation of the `json:",omitzero"` tag, so removing it may affect
|
||||
program behavior. For this reason, the omitzero modernizer will not
|
||||
make changes in any package that contains +kubebuilder annotations.
|
||||
|
||||
Replacing `omitempty` with `omitzero` is a change in behavior. The
|
||||
original code would always encode the struct field, whereas the
|
||||
modified code will omit it if it is a zero-value.
|
||||
|
||||
158
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/omitzero.go
generated
vendored
158
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/omitzero.go
generated
vendored
@ -9,6 +9,8 @@ import (
|
||||
"go/types"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
@ -25,82 +27,106 @@ var OmitZeroAnalyzer = &analysis.Analyzer{
|
||||
URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#omitzero",
|
||||
}
|
||||
|
||||
func checkOmitEmptyField(pass *analysis.Pass, info *types.Info, curField *ast.Field) {
|
||||
typ := info.TypeOf(curField.Type)
|
||||
_, ok := typ.Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
// Not a struct
|
||||
return
|
||||
}
|
||||
tag := curField.Tag
|
||||
if tag == nil {
|
||||
// No tag to check
|
||||
return
|
||||
}
|
||||
// The omitempty tag may be used by other packages besides json, but we should only modify its use with json
|
||||
tagconv, _ := strconv.Unquote(tag.Value)
|
||||
match := omitemptyRegex.FindStringSubmatchIndex(tagconv)
|
||||
if match == nil {
|
||||
// No omitempty in json tag
|
||||
return
|
||||
}
|
||||
omitEmpty, err := astutil.RangeInStringLiteral(curField.Tag, match[2], match[3])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var remove analysis.Range = omitEmpty
|
||||
|
||||
jsonTag := reflect.StructTag(tagconv).Get("json")
|
||||
if jsonTag == ",omitempty" {
|
||||
// Remove the entire struct tag if json is the only package used
|
||||
if match[1]-match[0] == len(tagconv) {
|
||||
remove = curField.Tag
|
||||
} else {
|
||||
// Remove the json tag if omitempty is the only field
|
||||
remove, err = astutil.RangeInStringLiteral(curField.Tag, match[0], match[1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
pass.Report(analysis.Diagnostic{
|
||||
Pos: curField.Tag.Pos(),
|
||||
End: curField.Tag.End(),
|
||||
Message: "Omitempty has no effect on nested struct fields",
|
||||
SuggestedFixes: []analysis.SuggestedFix{
|
||||
{
|
||||
Message: "Remove redundant omitempty tag",
|
||||
TextEdits: []analysis.TextEdit{
|
||||
{
|
||||
Pos: remove.Pos(),
|
||||
End: remove.End(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Replace omitempty with omitzero (behavior change)",
|
||||
TextEdits: []analysis.TextEdit{
|
||||
{
|
||||
Pos: omitEmpty.Pos(),
|
||||
End: omitEmpty.End(),
|
||||
NewText: []byte(",omitzero"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}})
|
||||
}
|
||||
|
||||
// The omitzero pass searches for instances of "omitempty" in a json field tag on a
|
||||
// struct. Since "omitfilesUsingGoVersions not have any effect when applied to a struct field,
|
||||
// it suggests either deleting "omitempty" or replacing it with "omitzero", which
|
||||
// correctly excludes structs from a json encoding.
|
||||
func omitzero(pass *analysis.Pass) (any, error) {
|
||||
// usesKubebuilder reports whether "+kubebuilder:" appears in
|
||||
// any comment in the package, since it has its own
|
||||
// interpretation of what omitzero means; see go.dev/issue/76649.
|
||||
// It is computed once, on demand.
|
||||
usesKubebuilder := sync.OnceValue[bool](func() bool {
|
||||
for _, file := range pass.Files {
|
||||
for _, comment := range file.Comments {
|
||||
if strings.Contains(comment.Text(), "+kubebuilder:") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
checkField := func(field *ast.Field) {
|
||||
typ := pass.TypesInfo.TypeOf(field.Type)
|
||||
_, ok := typ.Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
// Not a struct
|
||||
return
|
||||
}
|
||||
tag := field.Tag
|
||||
if tag == nil {
|
||||
// No tag to check
|
||||
return
|
||||
}
|
||||
// The omitempty tag may be used by other packages besides json, but we should only modify its use with json
|
||||
tagconv, _ := strconv.Unquote(tag.Value)
|
||||
match := omitemptyRegex.FindStringSubmatchIndex(tagconv)
|
||||
if match == nil {
|
||||
// No omitempty in json tag
|
||||
return
|
||||
}
|
||||
omitEmpty, err := astutil.RangeInStringLiteral(field.Tag, match[2], match[3])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var remove analysis.Range = omitEmpty
|
||||
|
||||
jsonTag := reflect.StructTag(tagconv).Get("json")
|
||||
if jsonTag == ",omitempty" {
|
||||
// Remove the entire struct tag if json is the only package used
|
||||
if match[1]-match[0] == len(tagconv) {
|
||||
remove = field.Tag
|
||||
} else {
|
||||
// Remove the json tag if omitempty is the only field
|
||||
remove, err = astutil.RangeInStringLiteral(field.Tag, match[0], match[1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't offer a fix if the package seems to use kubebuilder,
|
||||
// as it has its own intepretation of "omitzero" tags.
|
||||
// https://book.kubebuilder.io/reference/markers.html
|
||||
if usesKubebuilder() {
|
||||
return
|
||||
}
|
||||
|
||||
pass.Report(analysis.Diagnostic{
|
||||
Pos: field.Tag.Pos(),
|
||||
End: field.Tag.End(),
|
||||
Message: "Omitempty has no effect on nested struct fields",
|
||||
SuggestedFixes: []analysis.SuggestedFix{
|
||||
{
|
||||
Message: "Remove redundant omitempty tag",
|
||||
TextEdits: []analysis.TextEdit{
|
||||
{
|
||||
Pos: remove.Pos(),
|
||||
End: remove.End(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Replace omitempty with omitzero (behavior change)",
|
||||
TextEdits: []analysis.TextEdit{
|
||||
{
|
||||
Pos: omitEmpty.Pos(),
|
||||
End: omitEmpty.End(),
|
||||
NewText: []byte(",omitzero"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}})
|
||||
}
|
||||
|
||||
for curFile := range filesUsingGoVersion(pass, versions.Go1_24) {
|
||||
for curStruct := range curFile.Preorder((*ast.StructType)(nil)) {
|
||||
for _, curField := range curStruct.Node().(*ast.StructType).Fields.List {
|
||||
checkOmitEmptyField(pass, pass.TypesInfo, curField)
|
||||
checkField(curField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
132
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go
generated
vendored
132
src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go
generated
vendored
@ -50,9 +50,14 @@ func init() {
|
||||
// The following must hold for a replacement to occur:
|
||||
//
|
||||
// 1. All instances of i and s must be in one of these forms.
|
||||
// Binary expressions:
|
||||
// (a): establishing that i < 0: e.g.: i < 0, 0 > i, i == -1, -1 == i
|
||||
// (b): establishing that i > -1: e.g.: i >= 0, 0 <= i, i == 0, 0 == i
|
||||
//
|
||||
// Binary expressions must be inequalities equivalent to
|
||||
// "Index failed" (e.g. i < 0) or "Index succeeded" (i >= 0),
|
||||
// or identities such as these (and their negations):
|
||||
//
|
||||
// 0 > i (flips left and right)
|
||||
// i <= -1, -1 >= i (replace strict inequality by non-strict)
|
||||
// i == -1, -1 == i (Index() guarantees i < 0 => i == -1)
|
||||
//
|
||||
// Slice expressions:
|
||||
// a: s[:i], s[0:i]
|
||||
@ -86,9 +91,9 @@ func init() {
|
||||
// use(before, after)
|
||||
// }
|
||||
//
|
||||
// If the condition involving `i` establishes that i > -1, then we replace it with
|
||||
// `if ok“. Variants listed above include i >= 0, i > 0, and i == 0.
|
||||
// If the condition is negated (e.g. establishes `i < 0`), we use `if !ok` instead.
|
||||
// If the condition involving `i` is equivalent to i >= 0, then we replace it with
|
||||
// `if ok“.
|
||||
// If the condition is negated (e.g. equivalent to `i < 0`), we use `if !ok` instead.
|
||||
// If the slices of `s` match `s[:i]` or `s[i+len(substr):]` or their variants listed above,
|
||||
// then we replace them with before and after.
|
||||
//
|
||||
@ -178,16 +183,16 @@ func stringscut(pass *analysis.Pass) (any, error) {
|
||||
// len(substr)]), then we can replace the call to Index()
|
||||
// with a call to Cut() and use the returned ok, before,
|
||||
// and after variables accordingly.
|
||||
lessZero, greaterNegOne, beforeSlice, afterSlice := checkIdxUses(pass.TypesInfo, index.Uses(iObj), s, substr)
|
||||
negative, nonnegative, beforeSlice, afterSlice := checkIdxUses(pass.TypesInfo, index.Uses(iObj), s, substr)
|
||||
|
||||
// Either there are no uses of before, after, or ok, or some use
|
||||
// of i does not match our criteria - don't suggest a fix.
|
||||
if lessZero == nil && greaterNegOne == nil && beforeSlice == nil && afterSlice == nil {
|
||||
if negative == nil && nonnegative == nil && beforeSlice == nil && afterSlice == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// If the only uses are ok and !ok, don't suggest a Cut() fix - these should be using Contains()
|
||||
isContains := (len(lessZero) > 0 || len(greaterNegOne) > 0) && len(beforeSlice) == 0 && len(afterSlice) == 0
|
||||
isContains := (len(negative) > 0 || len(nonnegative) > 0) && len(beforeSlice) == 0 && len(afterSlice) == 0
|
||||
|
||||
scope := iObj.Parent()
|
||||
var (
|
||||
@ -200,7 +205,7 @@ func stringscut(pass *analysis.Pass) (any, error) {
|
||||
|
||||
// If there will be no uses of ok, before, or after, use the
|
||||
// blank identifier instead.
|
||||
if len(lessZero) == 0 && len(greaterNegOne) == 0 {
|
||||
if len(negative) == 0 && len(nonnegative) == 0 {
|
||||
okVarName = "_"
|
||||
}
|
||||
if len(beforeSlice) == 0 {
|
||||
@ -226,8 +231,8 @@ func stringscut(pass *analysis.Pass) (any, error) {
|
||||
replacedFunc := "Cut"
|
||||
if isContains {
|
||||
replacedFunc = "Contains"
|
||||
replace(lessZero, "!"+foundVarName) // idx < 0 -> !found
|
||||
replace(greaterNegOne, foundVarName) // idx > -1 -> found
|
||||
replace(negative, "!"+foundVarName) // idx < 0 -> !found
|
||||
replace(nonnegative, foundVarName) // idx > -1 -> found
|
||||
|
||||
// Replace the assignment with found, and replace the call to
|
||||
// Index or IndexByte with a call to Contains.
|
||||
@ -244,8 +249,8 @@ func stringscut(pass *analysis.Pass) (any, error) {
|
||||
NewText: []byte("Contains"),
|
||||
})
|
||||
} else {
|
||||
replace(lessZero, "!"+okVarName) // idx < 0 -> !ok
|
||||
replace(greaterNegOne, okVarName) // idx > -1 -> ok
|
||||
replace(negative, "!"+okVarName) // idx < 0 -> !ok
|
||||
replace(nonnegative, okVarName) // idx > -1 -> ok
|
||||
replace(beforeSlice, beforeVarName) // s[:idx] -> before
|
||||
replace(afterSlice, afterVarName) // s[idx+k:] -> after
|
||||
|
||||
@ -364,11 +369,11 @@ func indexArgValid(info *types.Info, index *typeindex.Index, expr ast.Expr, afte
|
||||
// one of the following four valid formats, it returns a list of occurrences for
|
||||
// each format. If any of the uses do not match one of the formats, return nil
|
||||
// for all values, since we should not offer a replacement.
|
||||
// 1. lessZero - a condition involving i establishing that i is negative (e.g. i < 0, 0 > i, i == -1, -1 == i)
|
||||
// 2. greaterNegOne - a condition involving i establishing that i is non-negative (e.g. i >= 0, 0 <= i, i == 0, 0 == i)
|
||||
// 1. negative - a condition equivalent to i < 0
|
||||
// 2. nonnegative - a condition equivalent to i >= 0
|
||||
// 3. beforeSlice - a slice of `s` that matches either s[:i], s[0:i]
|
||||
// 4. afterSlice - a slice of `s` that matches one of: s[i+len(substr):], s[len(substr) + i:], s[i + const], s[k + i] (where k = len(substr))
|
||||
func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr ast.Expr) (lessZero, greaterNegOne, beforeSlice, afterSlice []ast.Expr) {
|
||||
func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr ast.Expr) (negative, nonnegative, beforeSlice, afterSlice []ast.Expr) {
|
||||
use := func(cur inspector.Cursor) bool {
|
||||
ek, _ := cur.ParentEdge()
|
||||
n := cur.Parent().Node()
|
||||
@ -377,13 +382,13 @@ func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr a
|
||||
check := n.(*ast.BinaryExpr)
|
||||
switch checkIdxComparison(info, check) {
|
||||
case -1:
|
||||
lessZero = append(lessZero, check)
|
||||
negative = append(negative, check)
|
||||
return true
|
||||
case 1:
|
||||
greaterNegOne = append(greaterNegOne, check)
|
||||
nonnegative = append(nonnegative, check)
|
||||
return true
|
||||
}
|
||||
// Check does not establish that i < 0 or i > -1.
|
||||
// Check is not equivalent to that i < 0 or i >= 0.
|
||||
// Might be part of an outer slice expression like s[i + k]
|
||||
// which requires a different check.
|
||||
// Check that the thing being sliced is s and that the slice
|
||||
@ -421,7 +426,7 @@ func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr a
|
||||
return nil, nil, nil, nil
|
||||
}
|
||||
}
|
||||
return lessZero, greaterNegOne, beforeSlice, afterSlice
|
||||
return negative, nonnegative, beforeSlice, afterSlice
|
||||
}
|
||||
|
||||
// hasModifyingUses reports whether any of the uses involve potential
|
||||
@ -451,52 +456,57 @@ func hasModifyingUses(info *types.Info, uses iter.Seq[inspector.Cursor], afterPo
|
||||
return false
|
||||
}
|
||||
|
||||
// checkIdxComparison reports whether the check establishes that i is negative
|
||||
// or non-negative. It returns -1 in the first case, 1 in the second, and 0 if
|
||||
// we can confirm neither condition. We assume that a check passed to
|
||||
// checkIdxComparison has i as one of its operands.
|
||||
// checkIdxComparison reports whether the check is equivalent to i < 0 or its negation, or neither.
|
||||
// For equivalent to i >= 0, we only accept this exact BinaryExpr since
|
||||
// expressions like i > 0 or i >= 1 make a stronger statement about the value of i.
|
||||
// We avoid suggesting a fix in this case since it may result in an invalid
|
||||
// transformation (See golang/go#76687).
|
||||
// Since strings.Index returns exactly -1 if the substring is not found, we
|
||||
// don't need to handle expressions like i <= -3.
|
||||
// We return 0 if the expression does not match any of these options.
|
||||
// We assume that a check passed to checkIdxComparison has i as one of its operands.
|
||||
func checkIdxComparison(info *types.Info, check *ast.BinaryExpr) int {
|
||||
// Check establishes that i is negative.
|
||||
// e.g.: i < 0, 0 > i, i == -1, -1 == i
|
||||
if check.Op == token.LSS && (isNegativeConst(info, check.Y) || isZeroIntConst(info, check.Y)) || //i < (0 or neg)
|
||||
check.Op == token.GTR && (isNegativeConst(info, check.X) || isZeroIntConst(info, check.X)) || // (0 or neg) > i
|
||||
check.Op == token.LEQ && (isNegativeConst(info, check.Y)) || //i <= (neg)
|
||||
check.Op == token.GEQ && (isNegativeConst(info, check.X)) || // (neg) >= i
|
||||
check.Op == token.EQL &&
|
||||
(isNegativeConst(info, check.X) || isNegativeConst(info, check.Y)) { // i == neg; neg == i
|
||||
return -1
|
||||
// Ensure that the constant (if any) is on the right.
|
||||
x, op, y := check.X, check.Op, check.Y
|
||||
if info.Types[x].Value != nil {
|
||||
x, op, y = y, flip(op), x
|
||||
}
|
||||
// Check establishes that i is non-negative.
|
||||
// e.g.: i >= 0, 0 <= i, i == 0, 0 == i
|
||||
if check.Op == token.GTR && (isNonNegativeConst(info, check.Y) || isIntLiteral(info, check.Y, -1)) || // i > (non-neg or -1)
|
||||
check.Op == token.LSS && (isNonNegativeConst(info, check.X) || isIntLiteral(info, check.X, -1)) || // (non-neg or -1) < i
|
||||
check.Op == token.GEQ && isNonNegativeConst(info, check.Y) || // i >= (non-neg)
|
||||
check.Op == token.LEQ && isNonNegativeConst(info, check.X) || // (non-neg) <= i
|
||||
check.Op == token.EQL &&
|
||||
(isNonNegativeConst(info, check.X) || isNonNegativeConst(info, check.Y)) { // i == non-neg; non-neg == i
|
||||
return 1
|
||||
|
||||
yIsInt := func(k int64) bool {
|
||||
return isIntLiteral(info, y, k)
|
||||
}
|
||||
return 0
|
||||
|
||||
if op == token.LSS && yIsInt(0) || // i < 0
|
||||
op == token.EQL && yIsInt(-1) || // i == -1
|
||||
op == token.LEQ && yIsInt(-1) { // i <= -1
|
||||
return -1 // check <=> i is negative
|
||||
}
|
||||
|
||||
if op == token.GEQ && yIsInt(0) || // i >= 0
|
||||
op == token.NEQ && yIsInt(-1) || // i != -1
|
||||
op == token.GTR && yIsInt(-1) { // i > -1
|
||||
return +1 // check <=> i is non-negative
|
||||
}
|
||||
|
||||
return 0 // unknown
|
||||
}
|
||||
|
||||
// isNegativeConst returns true if the expr is a const int with value < zero.
|
||||
func isNegativeConst(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 {
|
||||
return v < 0
|
||||
}
|
||||
// flip changes the comparison token as if the operands were flipped.
|
||||
// It is defined only for == and the four inequalities.
|
||||
func flip(op token.Token) token.Token {
|
||||
switch op {
|
||||
case token.EQL:
|
||||
return token.EQL // (same)
|
||||
case token.GEQ:
|
||||
return token.LEQ
|
||||
case token.GTR:
|
||||
return token.LSS
|
||||
case token.LEQ:
|
||||
return token.GEQ
|
||||
case token.LSS:
|
||||
return token.GTR
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return v >= 0
|
||||
}
|
||||
}
|
||||
return false
|
||||
return op
|
||||
}
|
||||
|
||||
// isBeforeSlice reports whether the SliceExpr is of the form s[:i] or s[0:i].
|
||||
|
||||
41
src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go
generated
vendored
41
src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go
generated
vendored
@ -27,6 +27,7 @@ package unitchecker
|
||||
// printf checker.
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
@ -74,6 +75,7 @@ type Config struct {
|
||||
VetxOnly bool // run analysis only for facts, not diagnostics
|
||||
VetxOutput string // where to write file of fact information
|
||||
Stdout string // write stdout (e.g. JSON, unified diff) to this file
|
||||
FixArchive string // write fixed files to this zip archive, if non-empty
|
||||
SucceedOnTypecheckFailure bool // obsolete awful hack; see #18395 and below
|
||||
}
|
||||
|
||||
@ -153,7 +155,7 @@ func Run(configFile string, analyzers []*analysis.Analyzer) {
|
||||
|
||||
// In VetxOnly mode, the analysis is run only for facts.
|
||||
if !cfg.VetxOnly {
|
||||
code = processResults(fset, cfg.ID, results)
|
||||
code = processResults(fset, cfg.ID, cfg.FixArchive, results)
|
||||
}
|
||||
|
||||
os.Exit(code)
|
||||
@ -177,7 +179,7 @@ func readConfig(filename string) (*Config, error) {
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func processResults(fset *token.FileSet, id string, results []result) (exit int) {
|
||||
func processResults(fset *token.FileSet, id, fixArchive string, results []result) (exit int) {
|
||||
if analysisflags.Fix {
|
||||
// Don't print the diagnostics,
|
||||
// but apply all fixes from the root actions.
|
||||
@ -194,7 +196,40 @@ func processResults(fset *token.FileSet, id string, results []result) (exit int)
|
||||
Diagnostics: res.diagnostics,
|
||||
}
|
||||
}
|
||||
if err := driverutil.ApplyFixes(fixActions, analysisflags.Diff, false); err != nil {
|
||||
|
||||
// By default, fixes overwrite the original file.
|
||||
// With the -diff flag, print the diffs to stdout.
|
||||
// If "go fix" provides a fix archive, we write files
|
||||
// into it so that mutations happen after the build.
|
||||
write := func(filename string, content []byte) error {
|
||||
return os.WriteFile(filename, content, 0644)
|
||||
}
|
||||
if fixArchive != "" {
|
||||
f, err := os.Create(fixArchive)
|
||||
if err != nil {
|
||||
log.Fatalf("can't create -fix archive: %v", err)
|
||||
}
|
||||
zw := zip.NewWriter(f)
|
||||
zw.SetComment(id) // ignore error
|
||||
defer func() {
|
||||
if err := zw.Close(); err != nil {
|
||||
log.Fatalf("closing -fix archive zip writer: %v", err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
log.Fatalf("closing -fix archive file: %v", err)
|
||||
}
|
||||
}()
|
||||
write = func(filename string, content []byte) error {
|
||||
f, err := zw.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(content)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := driverutil.ApplyFixes(fixActions, write, analysisflags.Diff, false); err != nil {
|
||||
// Fail when applying fixes failed.
|
||||
log.Print(err)
|
||||
exit = 1
|
||||
|
||||
13
src/cmd/vendor/golang.org/x/tools/internal/analysis/driverutil/fix.go
generated
vendored
13
src/cmd/vendor/golang.org/x/tools/internal/analysis/driverutil/fix.go
generated
vendored
@ -93,11 +93,13 @@ type FixAction struct {
|
||||
//
|
||||
// If printDiff (from the -diff flag) is set, instead of updating the
|
||||
// files it display the final patch composed of all the cleanly merged
|
||||
// fixes.
|
||||
// fixes. (It is tempting to factor printDiff as just a variant of
|
||||
// writeFile that is provided the old and new content, but it's hard
|
||||
// to generate a good summary that way.)
|
||||
//
|
||||
// TODO(adonovan): handle file-system level aliases such as symbolic
|
||||
// links using robustio.FileID.
|
||||
func ApplyFixes(actions []FixAction, printDiff, verbose bool) error {
|
||||
func ApplyFixes(actions []FixAction, writeFile func(filename string, content []byte) error, printDiff, verbose bool) error {
|
||||
generated := make(map[*token.File]bool)
|
||||
|
||||
// Select fixes to apply.
|
||||
@ -264,12 +266,11 @@ fixloop:
|
||||
os.Stdout.WriteString(unified)
|
||||
|
||||
} else {
|
||||
// write
|
||||
// write file
|
||||
totalFiles++
|
||||
// TODO(adonovan): abstract the I/O.
|
||||
if err := os.WriteFile(file, final, 0644); err != nil {
|
||||
if err := writeFile(file, final); err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
continue // (causes ApplyFix to return an error)
|
||||
}
|
||||
filesUpdated++
|
||||
}
|
||||
|
||||
44
src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/callee.go
generated
vendored
44
src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/callee.go
generated
vendored
@ -725,8 +725,48 @@ func analyzeAssignment(info *types.Info, stack []ast.Node) (assignable, ifaceAss
|
||||
paramType := paramTypeAtIndex(sig, call, i)
|
||||
ifaceAssign := paramType == nil || types.IsInterface(paramType)
|
||||
affectsInference := false
|
||||
if fn := typeutil.StaticCallee(info, call); fn != nil {
|
||||
if sig2 := fn.Type().(*types.Signature); sig2.Recv() == nil {
|
||||
switch callee := typeutil.Callee(info, call).(type) {
|
||||
case *types.Builtin:
|
||||
// Consider this litmus test:
|
||||
//
|
||||
// func f(x int64) any { return max(x) }
|
||||
// func main() { fmt.Printf("%T", f(42)) }
|
||||
//
|
||||
// If we lose the implicit conversion from untyped int
|
||||
// to int64, the type inferred for the max(x) call changes,
|
||||
// resulting in a different dynamic behavior: it prints
|
||||
// int, not int64.
|
||||
//
|
||||
// Inferred result type affected:
|
||||
// new
|
||||
// complex, real, imag
|
||||
// min, max
|
||||
//
|
||||
// Dynamic behavior change:
|
||||
// append -- dynamic type of append([]any(nil), x)[0]
|
||||
// delete(m, x) -- dynamic key type where m is map[any]unit
|
||||
// panic -- dynamic type of panic value
|
||||
//
|
||||
// Unaffected:
|
||||
// recover
|
||||
// make
|
||||
// len, cap
|
||||
// clear
|
||||
// close
|
||||
// copy
|
||||
// print, println -- only uses underlying types (?)
|
||||
//
|
||||
// The dynamic type cases are all covered by
|
||||
// the ifaceAssign logic.
|
||||
switch callee.Name() {
|
||||
case "new", "complex", "real", "imag", "min", "max":
|
||||
affectsInference = true
|
||||
}
|
||||
|
||||
case *types.Func:
|
||||
// Only standalone (non-method) functions have type
|
||||
// parameters affected by the call arguments.
|
||||
if sig2 := callee.Signature(); sig2.Recv() == nil {
|
||||
originParamType := paramTypeAtIndex(sig2, call, i)
|
||||
affectsInference = originParamType == nil || new(typeparams.Free).Has(originParamType)
|
||||
}
|
||||
|
||||
2
src/cmd/vendor/modules.txt
vendored
2
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.20251130212600-1ad6f3d02713
|
||||
# golang.org/x/tools v0.39.1-0.20251205000126-062ef7b6ced2
|
||||
## explicit; go 1.24.0
|
||||
golang.org/x/tools/cmd/bisect
|
||||
golang.org/x/tools/cover
|
||||
|
||||
Loading…
Reference in New Issue
Block a user