diff options
| author | Dmitri Shuralyov <dmitshur@golang.org> | 2026-03-20 13:55:38 -0400 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2026-03-20 13:53:43 -0700 |
| commit | f2dae4c19d20070eeb2cef6baa5c20e0081f53f9 (patch) | |
| tree | e565327c235d95431bbd190e66aa20f533b1e25a /src/cmd | |
| parent | c8df1410d50f69b50eb5e643d15b6a3aab0ada06 (diff) | |
| download | go-f2dae4c19d20070eeb2cef6baa5c20e0081f53f9.tar.xz | |
all: update to x/tools@5d7afbc08aec
Pull in CL 757060 to get x/tools/cmd/bundle working again.
For #36905.
For #9859.
[git-generate]
cd src/cmd
go get golang.org/x/tools@v0.43.1-0.20260319213245-5d7afbc08aec # CL 757060
go get golang.org/x/sys@v0.42.1-0.20260320201212-a76ec62d6c53 # for #78259
go mod tidy
go mod vendor
cd ..
go get golang.org/x/sys@v0.42.1-0.20260320201212-a76ec62d6c53 # for consistency with the version in cmd
go mod tidy
go mod vendor
Change-Id: I27ec579e91923c8ea89c7f3a120f2220676a68c9
Reviewed-on: https://go-review.googlesource.com/c/go/+/757520
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Diffstat (limited to 'src/cmd')
45 files changed, 1176 insertions, 518 deletions
diff --git a/src/cmd/go.mod b/src/cmd/go.mod index 3bc9833971..d15db1306b 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -6,12 +6,12 @@ require ( github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 golang.org/x/arch v0.23.1-0.20260109160903-657d90bd6695 golang.org/x/build v0.0.0-20260122183339-3ba88df37c64 - golang.org/x/mod v0.32.0 - golang.org/x/sync v0.19.0 - golang.org/x/sys v0.40.1-0.20260116220947-d25a7aaff8c2 - golang.org/x/telemetry v0.0.0-20260116145544-c6413dc483f5 + golang.org/x/mod v0.34.0 + golang.org/x/sync v0.20.0 + golang.org/x/sys v0.42.1-0.20260320201212-a76ec62d6c53 + golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c golang.org/x/term v0.39.0 - golang.org/x/tools v0.41.1-0.20260122210857-a60613f0795e + golang.org/x/tools v0.43.1-0.20260319213245-5d7afbc08aec ) require ( diff --git a/src/cmd/go.sum b/src/cmd/go.sum index 224585feb6..23627773f0 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -10,19 +10,19 @@ golang.org/x/arch v0.23.1-0.20260109160903-657d90bd6695 h1:q45HsUyFzBjBk4mHGgUew golang.org/x/arch v0.23.1-0.20260109160903-657d90bd6695/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/build v0.0.0-20260122183339-3ba88df37c64 h1:BNhBATNmH/VtzGolB+ksQPPvn6ZyffiR8TmKenqNo+A= golang.org/x/build v0.0.0-20260122183339-3ba88df37c64/go.mod h1:3QmSbNil8ZWqC94m80Glej1v8b92gYzPIQPTtSa0c+4= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.40.1-0.20260116220947-d25a7aaff8c2 h1:jUsftZdK5NYH1tUlaQUfascfiJtOm7qHnRPWsG6hb4w= -golang.org/x/sys v0.40.1-0.20260116220947-d25a7aaff8c2/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20260116145544-c6413dc483f5 h1:i0p03B68+xC1kD2QUO8JzDTPXCzhN56OLJ+IhHY8U3A= -golang.org/x/telemetry v0.0.0-20260116145544-c6413dc483f5/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.42.1-0.20260320201212-a76ec62d6c53 h1:0T1C9w/fKTlj64Z8y0UtRoCAD7UZ+l5ZCSHcs3GYSCI= +golang.org/x/sys v0.42.1-0.20260320201212-a76ec62d6c53/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c h1:6a8FdnNk6bTXBjR4AGKFgUKuo+7GnR3FX5L7CbveeZc= +golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw= golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.33.1-0.20260122225119-3264de9174be h1:EwuAS7HtEmZVDSL0zq464yhyVIjdDETleE+K94kfwxg= golang.org/x/text v0.33.1-0.20260122225119-3264de9174be/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= -golang.org/x/tools v0.41.1-0.20260122210857-a60613f0795e h1:+CX08Gt13e9ttPSqktf4tM6Ft1hv8yedWyIfcwmakSQ= -golang.org/x/tools v0.41.1-0.20260122210857-a60613f0795e/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/tools v0.43.1-0.20260319213245-5d7afbc08aec h1:kTU64nIpH5vbfY0lQLyoZB98LkAmp2WzOvYoylbOIhg= +golang.org/x/tools v0.43.1-0.20260319213245-5d7afbc08aec/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef h1:mqLYrXCXYEZOop9/Dbo6RPX11539nwiCNBb1icVPmw8= rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ= diff --git a/src/cmd/vendor/golang.org/x/sys/plan9/syscall_plan9.go b/src/cmd/vendor/golang.org/x/sys/plan9/syscall_plan9.go index d079d8116e..761912237f 100644 --- a/src/cmd/vendor/golang.org/x/sys/plan9/syscall_plan9.go +++ b/src/cmd/vendor/golang.org/x/sys/plan9/syscall_plan9.go @@ -19,13 +19,7 @@ import ( // A Note is a string describing a process note. // It implements the os.Signal interface. -type Note string - -func (n Note) Signal() {} - -func (n Note) String() string { - return string(n) -} +type Note = syscall.Note var ( Stdin = 0 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/ztypes_linux.go b/src/cmd/vendor/golang.org/x/sys/unix/ztypes_linux.go index c1a4670171..45476a73c6 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -593,110 +593,115 @@ const ( ) const ( - NDA_UNSPEC = 0x0 - NDA_DST = 0x1 - NDA_LLADDR = 0x2 - NDA_CACHEINFO = 0x3 - NDA_PROBES = 0x4 - NDA_VLAN = 0x5 - NDA_PORT = 0x6 - NDA_VNI = 0x7 - NDA_IFINDEX = 0x8 - NDA_MASTER = 0x9 - NDA_LINK_NETNSID = 0xa - NDA_SRC_VNI = 0xb - NTF_USE = 0x1 - NTF_SELF = 0x2 - NTF_MASTER = 0x4 - NTF_PROXY = 0x8 - NTF_EXT_LEARNED = 0x10 - NTF_OFFLOADED = 0x20 - NTF_ROUTER = 0x80 - NUD_INCOMPLETE = 0x1 - NUD_REACHABLE = 0x2 - NUD_STALE = 0x4 - NUD_DELAY = 0x8 - NUD_PROBE = 0x10 - NUD_FAILED = 0x20 - NUD_NOARP = 0x40 - NUD_PERMANENT = 0x80 - NUD_NONE = 0x0 - IFA_UNSPEC = 0x0 - IFA_ADDRESS = 0x1 - IFA_LOCAL = 0x2 - IFA_LABEL = 0x3 - IFA_BROADCAST = 0x4 - IFA_ANYCAST = 0x5 - IFA_CACHEINFO = 0x6 - IFA_MULTICAST = 0x7 - IFA_FLAGS = 0x8 - IFA_RT_PRIORITY = 0x9 - IFA_TARGET_NETNSID = 0xa - IFAL_LABEL = 0x2 - IFAL_ADDRESS = 0x1 - RT_SCOPE_UNIVERSE = 0x0 - RT_SCOPE_SITE = 0xc8 - RT_SCOPE_LINK = 0xfd - RT_SCOPE_HOST = 0xfe - RT_SCOPE_NOWHERE = 0xff - RT_TABLE_UNSPEC = 0x0 - RT_TABLE_COMPAT = 0xfc - RT_TABLE_DEFAULT = 0xfd - RT_TABLE_MAIN = 0xfe - RT_TABLE_LOCAL = 0xff - RT_TABLE_MAX = 0xffffffff - RTA_UNSPEC = 0x0 - RTA_DST = 0x1 - RTA_SRC = 0x2 - RTA_IIF = 0x3 - RTA_OIF = 0x4 - RTA_GATEWAY = 0x5 - RTA_PRIORITY = 0x6 - RTA_PREFSRC = 0x7 - RTA_METRICS = 0x8 - RTA_MULTIPATH = 0x9 - RTA_FLOW = 0xb - RTA_CACHEINFO = 0xc - RTA_TABLE = 0xf - RTA_MARK = 0x10 - RTA_MFC_STATS = 0x11 - RTA_VIA = 0x12 - RTA_NEWDST = 0x13 - RTA_PREF = 0x14 - RTA_ENCAP_TYPE = 0x15 - RTA_ENCAP = 0x16 - RTA_EXPIRES = 0x17 - RTA_PAD = 0x18 - RTA_UID = 0x19 - RTA_TTL_PROPAGATE = 0x1a - RTA_IP_PROTO = 0x1b - RTA_SPORT = 0x1c - RTA_DPORT = 0x1d - RTN_UNSPEC = 0x0 - RTN_UNICAST = 0x1 - RTN_LOCAL = 0x2 - RTN_BROADCAST = 0x3 - RTN_ANYCAST = 0x4 - RTN_MULTICAST = 0x5 - RTN_BLACKHOLE = 0x6 - RTN_UNREACHABLE = 0x7 - RTN_PROHIBIT = 0x8 - RTN_THROW = 0x9 - RTN_NAT = 0xa - RTN_XRESOLVE = 0xb - SizeofNlMsghdr = 0x10 - SizeofNlMsgerr = 0x14 - SizeofRtGenmsg = 0x1 - SizeofNlAttr = 0x4 - SizeofRtAttr = 0x4 - SizeofIfInfomsg = 0x10 - SizeofIfAddrmsg = 0x8 - SizeofIfAddrlblmsg = 0xc - SizeofIfaCacheinfo = 0x10 - SizeofRtMsg = 0xc - SizeofRtNexthop = 0x8 - SizeofNdUseroptmsg = 0x10 - SizeofNdMsg = 0xc + NDA_UNSPEC = 0x0 + NDA_DST = 0x1 + NDA_LLADDR = 0x2 + NDA_CACHEINFO = 0x3 + NDA_PROBES = 0x4 + NDA_VLAN = 0x5 + NDA_PORT = 0x6 + NDA_VNI = 0x7 + NDA_IFINDEX = 0x8 + NDA_MASTER = 0x9 + NDA_LINK_NETNSID = 0xa + NDA_SRC_VNI = 0xb + NTF_USE = 0x1 + NTF_SELF = 0x2 + NTF_MASTER = 0x4 + NTF_PROXY = 0x8 + NTF_EXT_LEARNED = 0x10 + NTF_OFFLOADED = 0x20 + NTF_ROUTER = 0x80 + NUD_INCOMPLETE = 0x1 + NUD_REACHABLE = 0x2 + NUD_STALE = 0x4 + NUD_DELAY = 0x8 + NUD_PROBE = 0x10 + NUD_FAILED = 0x20 + NUD_NOARP = 0x40 + NUD_PERMANENT = 0x80 + NUD_NONE = 0x0 + IFA_UNSPEC = 0x0 + IFA_ADDRESS = 0x1 + IFA_LOCAL = 0x2 + IFA_LABEL = 0x3 + IFA_BROADCAST = 0x4 + IFA_ANYCAST = 0x5 + IFA_CACHEINFO = 0x6 + IFA_MULTICAST = 0x7 + IFA_FLAGS = 0x8 + IFA_RT_PRIORITY = 0x9 + IFA_TARGET_NETNSID = 0xa + IFAL_LABEL = 0x2 + IFAL_ADDRESS = 0x1 + RT_SCOPE_UNIVERSE = 0x0 + RT_SCOPE_SITE = 0xc8 + RT_SCOPE_LINK = 0xfd + RT_SCOPE_HOST = 0xfe + RT_SCOPE_NOWHERE = 0xff + RT_TABLE_UNSPEC = 0x0 + RT_TABLE_COMPAT = 0xfc + RT_TABLE_DEFAULT = 0xfd + RT_TABLE_MAIN = 0xfe + RT_TABLE_LOCAL = 0xff + RT_TABLE_MAX = 0xffffffff + RTA_UNSPEC = 0x0 + RTA_DST = 0x1 + RTA_SRC = 0x2 + RTA_IIF = 0x3 + RTA_OIF = 0x4 + RTA_GATEWAY = 0x5 + RTA_PRIORITY = 0x6 + RTA_PREFSRC = 0x7 + RTA_METRICS = 0x8 + RTA_MULTIPATH = 0x9 + RTA_FLOW = 0xb + RTA_CACHEINFO = 0xc + RTA_TABLE = 0xf + RTA_MARK = 0x10 + RTA_MFC_STATS = 0x11 + RTA_VIA = 0x12 + RTA_NEWDST = 0x13 + RTA_PREF = 0x14 + RTA_ENCAP_TYPE = 0x15 + RTA_ENCAP = 0x16 + RTA_EXPIRES = 0x17 + RTA_PAD = 0x18 + RTA_UID = 0x19 + RTA_TTL_PROPAGATE = 0x1a + RTA_IP_PROTO = 0x1b + RTA_SPORT = 0x1c + RTA_DPORT = 0x1d + RTN_UNSPEC = 0x0 + RTN_UNICAST = 0x1 + RTN_LOCAL = 0x2 + RTN_BROADCAST = 0x3 + RTN_ANYCAST = 0x4 + RTN_MULTICAST = 0x5 + RTN_BLACKHOLE = 0x6 + RTN_UNREACHABLE = 0x7 + RTN_PROHIBIT = 0x8 + RTN_THROW = 0x9 + RTN_NAT = 0xa + RTN_XRESOLVE = 0xb + PREFIX_UNSPEC = 0x0 + PREFIX_ADDRESS = 0x1 + PREFIX_CACHEINFO = 0x2 + SizeofNlMsghdr = 0x10 + SizeofNlMsgerr = 0x14 + SizeofRtGenmsg = 0x1 + SizeofNlAttr = 0x4 + SizeofRtAttr = 0x4 + SizeofIfInfomsg = 0x10 + SizeofPrefixmsg = 0xc + SizeofPrefixCacheinfo = 0x8 + SizeofIfAddrmsg = 0x8 + SizeofIfAddrlblmsg = 0xc + SizeofIfaCacheinfo = 0x10 + SizeofRtMsg = 0xc + SizeofRtNexthop = 0x8 + SizeofNdUseroptmsg = 0x10 + SizeofNdMsg = 0xc ) type NlMsghdr struct { @@ -735,6 +740,22 @@ type IfInfomsg struct { Change uint32 } +type Prefixmsg struct { + Family uint8 + Pad1 uint8 + Pad2 uint16 + Ifindex int32 + Type uint8 + Len uint8 + Flags uint8 + Pad3 uint8 +} + +type PrefixCacheinfo struct { + Preferred_time uint32 + Valid_time uint32 +} + type IfAddrmsg struct { Family uint8 Prefixlen uint8 diff --git a/src/cmd/vendor/golang.org/x/sys/windows/aliases.go b/src/cmd/vendor/golang.org/x/sys/windows/aliases.go index 16f90560a2..96317966e5 100644 --- a/src/cmd/vendor/golang.org/x/sys/windows/aliases.go +++ b/src/cmd/vendor/golang.org/x/sys/windows/aliases.go @@ -8,5 +8,6 @@ package windows import "syscall" +type Signal = syscall.Signal type Errno = syscall.Errno type SysProcAttr = syscall.SysProcAttr diff --git a/src/cmd/vendor/golang.org/x/sys/windows/syscall_windows.go b/src/cmd/vendor/golang.org/x/sys/windows/syscall_windows.go index 69439df2a4..d766436587 100644 --- a/src/cmd/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/src/cmd/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -900,6 +900,7 @@ const socket_error = uintptr(^uint32(0)) //sys NotifyRouteChange2(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) = iphlpapi.NotifyRouteChange2 //sys NotifyUnicastIpAddressChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) = iphlpapi.NotifyUnicastIpAddressChange //sys CancelMibChangeNotify2(notificationHandle Handle) (errcode error) = iphlpapi.CancelMibChangeNotify2 +//sys IsProcessorFeaturePresent(ProcessorFeature uint32) (ret bool) = kernel32.IsProcessorFeaturePresent // For testing: clients can set this flag to force // creation of IPv6 sockets to return EAFNOSUPPORT. @@ -1489,20 +1490,6 @@ func Getgid() (gid int) { return -1 } func Getegid() (egid int) { return -1 } func Getgroups() (gids []int, err error) { return nil, syscall.EWINDOWS } -type Signal int - -func (s Signal) Signal() {} - -func (s Signal) String() string { - if 0 <= s && int(s) < len(signals) { - str := signals[s] - if str != "" { - return str - } - } - return "signal " + itoa(int(s)) -} - func LoadCreateSymbolicLink() error { return procCreateSymbolicLinkW.Find() } diff --git a/src/cmd/vendor/golang.org/x/sys/windows/types_windows.go b/src/cmd/vendor/golang.org/x/sys/windows/types_windows.go index 6e4f50eb48..d5658a138c 100644 --- a/src/cmd/vendor/golang.org/x/sys/windows/types_windows.go +++ b/src/cmd/vendor/golang.org/x/sys/windows/types_windows.go @@ -3938,3 +3938,88 @@ const ( MOUSE_EVENT = 0x0002 WINDOW_BUFFER_SIZE_EVENT = 0x0004 ) + +// The processor features to be tested for IsProcessorFeaturePresent, see +// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent +const ( + PF_ARM_64BIT_LOADSTORE_ATOMIC = 25 + PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE = 24 + PF_ARM_EXTERNAL_CACHE_AVAILABLE = 26 + PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE = 27 + PF_ARM_VFP_32_REGISTERS_AVAILABLE = 18 + PF_3DNOW_INSTRUCTIONS_AVAILABLE = 7 + PF_CHANNELS_ENABLED = 16 + PF_COMPARE_EXCHANGE_DOUBLE = 2 + PF_COMPARE_EXCHANGE128 = 14 + PF_COMPARE64_EXCHANGE128 = 15 + PF_FASTFAIL_AVAILABLE = 23 + PF_FLOATING_POINT_EMULATED = 1 + PF_FLOATING_POINT_PRECISION_ERRATA = 0 + PF_MMX_INSTRUCTIONS_AVAILABLE = 3 + PF_NX_ENABLED = 12 + PF_PAE_ENABLED = 9 + PF_RDTSC_INSTRUCTION_AVAILABLE = 8 + PF_RDWRFSGSBASE_AVAILABLE = 22 + PF_SECOND_LEVEL_ADDRESS_TRANSLATION = 20 + PF_SSE3_INSTRUCTIONS_AVAILABLE = 13 + PF_SSSE3_INSTRUCTIONS_AVAILABLE = 36 + PF_SSE4_1_INSTRUCTIONS_AVAILABLE = 37 + PF_SSE4_2_INSTRUCTIONS_AVAILABLE = 38 + PF_AVX_INSTRUCTIONS_AVAILABLE = 39 + PF_AVX2_INSTRUCTIONS_AVAILABLE = 40 + PF_AVX512F_INSTRUCTIONS_AVAILABLE = 41 + PF_VIRT_FIRMWARE_ENABLED = 21 + PF_XMMI_INSTRUCTIONS_AVAILABLE = 6 + PF_XMMI64_INSTRUCTIONS_AVAILABLE = 10 + PF_XSAVE_ENABLED = 17 + PF_ARM_V8_INSTRUCTIONS_AVAILABLE = 29 + PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE = 30 + PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE = 31 + PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE = 34 + PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE = 43 + PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE = 44 + PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE = 45 + PF_ARM_SVE_INSTRUCTIONS_AVAILABLE = 46 + PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE = 47 + PF_ARM_SVE2_1_INSTRUCTIONS_AVAILABLE = 48 + PF_ARM_SVE_AES_INSTRUCTIONS_AVAILABLE = 49 + PF_ARM_SVE_PMULL128_INSTRUCTIONS_AVAILABLE = 50 + PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE = 51 + PF_ARM_SVE_BF16_INSTRUCTIONS_AVAILABLE = 52 + PF_ARM_SVE_EBF16_INSTRUCTIONS_AVAILABLE = 53 + PF_ARM_SVE_B16B16_INSTRUCTIONS_AVAILABLE = 54 + PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE = 55 + PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE = 56 + PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE = 57 + PF_ARM_SVE_F32MM_INSTRUCTIONS_AVAILABLE = 58 + PF_ARM_SVE_F64MM_INSTRUCTIONS_AVAILABLE = 59 + PF_BMI2_INSTRUCTIONS_AVAILABLE = 60 + PF_MOVDIR64B_INSTRUCTION_AVAILABLE = 61 + PF_ARM_LSE2_AVAILABLE = 62 + PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE = 64 + PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE = 65 + PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE = 66 + PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE = 67 + PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE = 68 + PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE = 69 + PF_ARM_SME_INSTRUCTIONS_AVAILABLE = 70 + PF_ARM_SME2_INSTRUCTIONS_AVAILABLE = 71 + PF_ARM_SME2_1_INSTRUCTIONS_AVAILABLE = 72 + PF_ARM_SME2_2_INSTRUCTIONS_AVAILABLE = 73 + PF_ARM_SME_AES_INSTRUCTIONS_AVAILABLE = 74 + PF_ARM_SME_SBITPERM_INSTRUCTIONS_AVAILABLE = 75 + PF_ARM_SME_SF8MM4_INSTRUCTIONS_AVAILABLE = 76 + PF_ARM_SME_SF8MM8_INSTRUCTIONS_AVAILABLE = 77 + PF_ARM_SME_SF8DP2_INSTRUCTIONS_AVAILABLE = 78 + PF_ARM_SME_SF8DP4_INSTRUCTIONS_AVAILABLE = 79 + PF_ARM_SME_SF8FMA_INSTRUCTIONS_AVAILABLE = 80 + PF_ARM_SME_F8F32_INSTRUCTIONS_AVAILABLE = 81 + PF_ARM_SME_F8F16_INSTRUCTIONS_AVAILABLE = 82 + PF_ARM_SME_F16F16_INSTRUCTIONS_AVAILABLE = 83 + PF_ARM_SME_B16B16_INSTRUCTIONS_AVAILABLE = 84 + PF_ARM_SME_F64F64_INSTRUCTIONS_AVAILABLE = 85 + PF_ARM_SME_I16I64_INSTRUCTIONS_AVAILABLE = 86 + PF_ARM_SME_LUTv2_INSTRUCTIONS_AVAILABLE = 87 + PF_ARM_SME_FA64_INSTRUCTIONS_AVAILABLE = 88 + PF_UMONITOR_INSTRUCTION_AVAILABLE = 89 +) diff --git a/src/cmd/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/src/cmd/vendor/golang.org/x/sys/windows/zsyscall_windows.go index f25b7308a1..fe7a4ea124 100644 --- a/src/cmd/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/src/cmd/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -320,6 +320,7 @@ var ( procGetVolumePathNamesForVolumeNameW = modkernel32.NewProc("GetVolumePathNamesForVolumeNameW") procGetWindowsDirectoryW = modkernel32.NewProc("GetWindowsDirectoryW") procInitializeProcThreadAttributeList = modkernel32.NewProc("InitializeProcThreadAttributeList") + procIsProcessorFeaturePresent = modkernel32.NewProc("IsProcessorFeaturePresent") procIsWow64Process = modkernel32.NewProc("IsWow64Process") procIsWow64Process2 = modkernel32.NewProc("IsWow64Process2") procLoadLibraryExW = modkernel32.NewProc("LoadLibraryExW") @@ -2786,6 +2787,12 @@ func initializeProcThreadAttributeList(attrlist *ProcThreadAttributeList, attrco return } +func IsProcessorFeaturePresent(ProcessorFeature uint32) (ret bool) { + r0, _, _ := syscall.SyscallN(procIsProcessorFeaturePresent.Addr(), uintptr(ProcessorFeature)) + ret = r0 != 0 + return +} + func IsWow64Process(handle Handle, isWow64 *bool) (err error) { var _p0 uint32 if *isWow64 { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go index a7df4d1fe4..786c29d55d 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go @@ -11,6 +11,7 @@ import ( "go/token" "go/types" "reflect" + "time" ) // An Analyzer describes an analysis function and its options. @@ -250,7 +251,19 @@ type Fact interface { // A Module describes the module to which a package belongs. type Module struct { - Path string // module path - Version string // module version ("" if unknown, such as for workspace modules) - GoVersion string // go version used in module (e.g. "go1.22.0") + Path string // module path + Version string // module version ("" if unknown, such as for workspace modules) + Replace *Module // replaced by this module + Time *time.Time // time version was created + Main bool // is this the main module? + Indirect bool // is this module only an indirect dependency of main module? + Dir string // directory holding files for this module, if any + GoMod string // path to go.mod file used when loading this module, if any + GoVersion string // go version used in module (e.g. "go1.22.0") + Error *ModuleError // error loading module +} + +// ModuleError holds errors loading a module. +type ModuleError struct { + Err string // the error itself } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go index c7637df00a..2c4a8e7339 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go @@ -52,7 +52,7 @@ func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer { prefix = a.Name + "." enable := new(triState) - enableUsage := "enable " + a.Name + " analysis" + enableUsage := fmt.Sprintf("enable %q analysis", a.Name) flag.Var(enable, a.Name, enableUsage) enabled[a] = enable } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go index 9049145e22..415058db80 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go @@ -315,12 +315,43 @@ func (a *analyzer) inlineAlias(tn *types.TypeName, curId inspector.Cursor) { curPath := a.pass.Pkg.Path() curFile := astutil.EnclosingFile(curId) id := curId.Node().(*ast.Ident) + + // Find the complete identifier, which may take any of these forms: + // Id + // Id[T] + // Id[K, V] + // pkg.Id + // pkg.Id[T] + // pkg.Id[K, V] + var expr ast.Expr = id + if curId.ParentEdgeKind() == edge.SelectorExpr_Sel { + curId = curId.Parent() + expr = curId.Node().(ast.Expr) + } + // If expr is part of an IndexExpr or IndexListExpr, we'll need that node. + // Given C[int], TypeOf(C) is generic but TypeOf(C[int]) is instantiated. + switch curId.ParentEdgeKind() { + case edge.IndexExpr_X: + expr = curId.Parent().Node().(*ast.IndexExpr) + case edge.IndexListExpr_X: + expr = curId.Parent().Node().(*ast.IndexListExpr) + } + t := a.pass.TypesInfo.TypeOf(expr).(*types.Alias) // type of entire identifier + if targs := t.TypeArgs(); targs.Len() > 0 { + // Instantiate the alias with the type args from this use. + // For example, given type A = M[K, V], compute the type of the use + // A[int, Foo] as M[int, Foo]. + // Don't validate instantiation: it can't panic unless we have a bug, + // in which case seeing the stack trace via telemetry would be helpful. + instAlias, _ := types.Instantiate(nil, alias, slices.Collect(targs.Types()), false) + rhs = instAlias.(*types.Alias).Rhs() + } + // We have an identifier A here (n), possibly qualified by a package // identifier (sel.n), and an inlinable "type A = rhs" elsewhere. // // We can replace A with rhs if no name in rhs is shadowed at n's position, // and every package in rhs is importable by the current package. - var ( importPrefixes = map[string]string{curPath: ""} // from pkg path to prefix edits []analysis.TextEdit @@ -349,6 +380,7 @@ func (a *analyzer) inlineAlias(tn *types.TypeName, curId inspector.Cursor) { return } else if _, ok := importPrefixes[pkgPath]; !ok { // Use AddImport to add pkgPath if it's not there already. Associate the prefix it assigns + // with the prefix it assigns // with the package path for use by the TypeString qualifier below. prefix, eds := refactor.AddImport( a.pass.TypesInfo, curFile, pkgName, pkgPath, tn.Name(), id.Pos()) @@ -356,36 +388,7 @@ func (a *analyzer) inlineAlias(tn *types.TypeName, curId inspector.Cursor) { edits = append(edits, eds...) } } - // Find the complete identifier, which may take any of these forms: - // Id - // Id[T] - // Id[K, V] - // pkg.Id - // pkg.Id[T] - // pkg.Id[K, V] - var expr ast.Expr = id - if astutil.IsChildOf(curId, edge.SelectorExpr_Sel) { - curId = curId.Parent() - expr = curId.Node().(ast.Expr) - } - // If expr is part of an IndexExpr or IndexListExpr, we'll need that node. - // Given C[int], TypeOf(C) is generic but TypeOf(C[int]) is instantiated. - switch ek, _ := curId.ParentEdge(); ek { - case edge.IndexExpr_X: - expr = curId.Parent().Node().(*ast.IndexExpr) - case edge.IndexListExpr_X: - expr = curId.Parent().Node().(*ast.IndexListExpr) - } - t := a.pass.TypesInfo.TypeOf(expr).(*types.Alias) // type of entire identifier - if targs := t.TypeArgs(); targs.Len() > 0 { - // Instantiate the alias with the type args from this use. - // For example, given type A = M[K, V], compute the type of the use - // A[int, Foo] as M[int, Foo]. - // Don't validate instantiation: it can't panic unless we have a bug, - // in which case seeing the stack trace via telemetry would be helpful. - instAlias, _ := types.Instantiate(nil, alias, slices.Collect(targs.Types()), false) - rhs = instAlias.(*types.Alias).Rhs() - } + // To get the replacement text, render the alias RHS using the package prefixes // we assigned above. newText := types.TypeString(rhs, func(p *types.Package) string { @@ -526,7 +529,7 @@ func (a *analyzer) inlineConst(con *types.Const, cur inspector.Cursor) { } // If n is qualified by a package identifier, we'll need the full selector expression. var expr ast.Expr = n - if astutil.IsChildOf(cur, edge.SelectorExpr_Sel) { + if cur.ParentEdgeKind() == edge.SelectorExpr_Sel { expr = cur.Parent().Node().(ast.Expr) } a.reportInline("constant", "Constant", expr, edits, importPrefix+incon.RHSName) diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go index 28a5f6cd93..dfc4313679 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go @@ -16,7 +16,6 @@ import ( "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/cfg" "golang.org/x/tools/internal/analysis/analyzerutil" - "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/typesinternal" ) @@ -84,7 +83,7 @@ func runFunc(pass *analysis.Pass, node ast.Node) { // {FuncDecl,FuncLit,CallExpr,SelectorExpr}. // Find the set of cancel vars to analyze. - astutil.PreorderStack(node, nil, func(n ast.Node, stack []ast.Node) bool { + ast.PreorderStack(node, nil, func(n ast.Node, stack []ast.Node) bool { if _, ok := n.(*ast.FuncLit); ok && len(stack) > 0 { return false // don't stray into nested functions } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/atomictypes.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/atomictypes.go new file mode 100644 index 0000000000..2f314b2972 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/atomictypes.go @@ -0,0 +1,255 @@ +// Copyright 2026 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 modernize + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/edge" + "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/analysis/analyzerutil" + typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" + "golang.org/x/tools/internal/astutil" + "golang.org/x/tools/internal/goplsexport" + "golang.org/x/tools/internal/refactor" + "golang.org/x/tools/internal/typesinternal" + "golang.org/x/tools/internal/typesinternal/typeindex" + "golang.org/x/tools/internal/versions" +) + +var atomicTypesAnalyzer = &analysis.Analyzer{ + Name: "atomictypes", + Doc: analyzerutil.MustExtractDoc(doc, "atomictypes"), + Requires: []*analysis.Analyzer{ + inspect.Analyzer, + typeindexanalyzer.Analyzer, + }, + Run: runAtomic, + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#atomictypes", +} + +func init() { + // Export to gopls until this is a published modernizer. + goplsexport.AtomicTypesModernizer = atomicTypesAnalyzer +} + +// TODO(mkalil): support the Pointer variants. +// Consider the following function signatures for pointer loading: +// func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) +// func (x *Pointer[T]) Load() *T +// Since the former uses *unsafe.Pointer while the latter uses *Pointer[T], +// we would need to determine the type T to apply the transformation, and there +// will be additional edits required to remove any *unsafe.Pointer casts. +// "LoadPointer", "StorePointer", "SwapPointer", "CompareAndSwapPointer" + +// sync/atomic functions of interest. Some added in go1.19, some added in go1.23. +var syncAtomicFuncs = []string{ + // Added in go1.19. + "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr", + "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", + "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", + "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", + "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", + // Added in go1.23. + "AndInt32", "AndInt64", "AndUint32", "AndUint64", "AndUintptr", + "OrInt32", "OrInt64", "OrUint32", "OrUint64", "OrUintptr", +} + +func runAtomic(pass *analysis.Pass) (any, error) { + if !typesinternal.Imports(pass.Pkg, "sync/atomic") { + return nil, nil // doesn't directly import sync/atomic + } + + var ( + index = pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index) + info = pass.TypesInfo + ) + + // Gather all candidate variables v appearing + // in calls to atomic.AddInt32(&v, ...) et al. + var ( + atomicPkg *types.Package + vars = make(map[*types.Var]string) // maps candidate vars v to the name of the call they appear in + ) + for _, funcName := range syncAtomicFuncs { + obj := index.Object("sync/atomic", funcName) + if obj == nil { + continue + } + atomicPkg = obj.Pkg() + for curCall := range index.Calls(obj) { + call := curCall.Node().(*ast.CallExpr) + if unary, ok := call.Args[0].(*ast.UnaryExpr); ok && unary.Op == token.AND { + var v *types.Var + switch x := unary.X.(type) { + case *ast.Ident: + v, _ = info.Uses[x].(*types.Var) + case *ast.SelectorExpr: + if seln, ok := info.Selections[x]; ok { + v, _ = seln.Obj().(*types.Var) + } + } + if v != nil && !v.Exported() { + // v must be a non-exported package or local var, or a struct field. + switch v.Kind() { + case types.RecvVar, types.ParamVar, types.ResultVar: + continue // fix would change func signature + } + vars[v] = funcName + } + } + } + } + + // Check that all uses of each candidate variable + // appear in calls of the form atomic.AddInt32(&v, ...). +nextvar: + for v, funcName := range vars { + var edits []analysis.TextEdit + fixFiles := make(map[*ast.File]bool) // unique files involved in the current fix + + // Check the form of the declaration: var v int or struct { v int } + def, ok := index.Def(v) + if !ok { + continue + } + var ( + typ ast.Expr + names []*ast.Ident + ) + switch parent := def.Parent().Node().(type) { + case *ast.Field: // struct { v int } + names = parent.Names + typ = parent.Type + case *ast.ValueSpec: // var v int + if len(parent.Values) > 0 { + // e.g. var v int = 5 + // skip because rewriting as `var v atomic.Int32 = 5` is invalid + continue + } + names = parent.Names + typ = parent.Type + } + if len(names) != 1 || typ == nil { + continue // v is not the sole var declared here (e.g. var x, y int32); or no explicit type + } + oldType := info.TypeOf(typ) // e.g. "int32" + newType := strings.Title(oldType.Underlying().String()) // e.g. "Int32" + + // Get package prefix to avoid shadowing. + file := astutil.EnclosingFile(def) + pkgPrefix, impEdits := refactor.AddImport(pass.TypesInfo, file, "atomic", "sync/atomic", "", def.Node().Pos()) + if len(impEdits) > 0 { + panic("unexpected import edits") // atomic PkgName should be in scope already + } + // Edit the type. + // + // var v int32 + // ------------ + // var v atomic.Int32 + edits = append(edits, analysis.TextEdit{ + Pos: typ.Pos(), + End: typ.End(), + NewText: fmt.Appendf(nil, "%s%s", pkgPrefix, newType), + }) + fixFiles[file] = true + + // Each valid use is an Ident v or Selector expr.v within an atomic.F(&...) call. + var validUses []inspector.Cursor + for cur := range index.Uses(v) { + if v.IsField() && cur.ParentEdgeKind() == edge.KeyValueExpr_Key { + continue nextvar // we cannot fix initial an value assignment T{v: 1} + } + if cur.ParentEdgeKind() == edge.SelectorExpr_Sel { + cur = cur.Parent() // ascend from v to expr.v + } + // Inv: cur is the l-value expression denoting v. + // v must appear beneath atomic.AddInt32(&v, ...) call. + valid := false + if cur.ParentEdgeKind() == edge.UnaryExpr_X && + cur.Parent().Node().(*ast.UnaryExpr).Op == token.AND { + if ek, idx := cur.Parent().ParentEdge(); ek == edge.CallExpr_Args && idx == 0 { + curCall := cur.Parent().Parent() + call := curCall.Node().(*ast.CallExpr) + if fn, ok := typeutil.Callee(info, call).(*types.Func); ok && fn.Pkg() == atomicPkg { + valid = true + } + } + } + + if !valid { + // More complex case: reject candidate. + // + // For example, cur may be an unsynchronized load (e.g. v == 0). To + // avoid a type conversion error, we'd have to rewrite this as + // v.Load(). However, this is an invalid rewrite: if the program is + // mixing atomic operations with unsynchronized reads, the author + // might have accidentally introduced a data race and the suggested + // fix could obscure the mistake. Or, if the usage is intentional, + // rewriting may result in a behavior change. + continue nextvar + } + validUses = append(validUses, cur) + } + + for _, cur := range validUses { + vexpr := cur.Node() + call := cur.Parent().Parent().Node().(*ast.CallExpr) + fn := typeutil.Callee(info, call).(*types.Func) + // atomic.AddInt32(&v, ...) + // ----------------- ----- + // v.Add(...) + after := vexpr.End() // LoadInt32(&v⁁) + if len(call.Args) > 1 { + after = call.Args[1].Pos() // AddInt32(&v, ⁁...) + } + verb := strings.TrimSuffix(fn.Name(), newType) // "AddInt32" => "Add" + edits = append(edits, []analysis.TextEdit{ + { + Pos: call.Pos(), + End: vexpr.Pos(), + }, + { + Pos: vexpr.End(), + End: after, + NewText: fmt.Appendf(nil, ".%s(", verb), + }, + }...) + fixFiles[astutil.EnclosingFile(cur)] = true + } + + // Check minimum Go version: go1.19, or 1.23 for the And/Or functions. + if !(analyzerutil.FileUsesGoVersion(pass, file, versions.Go1_19) || + analyzerutil.FileUsesGoVersion(pass, file, versions.Go1_23) && + (strings.HasPrefix(funcName, "And") || strings.HasPrefix(funcName, "Or"))) { + continue + } + + // Skip if v is not local and the package has ignored files as it may be + // an incomplete transformation. + if !isLocal(v) && len(pass.IgnoredFiles) > 0 { + continue + } + + pass.Report(analysis.Diagnostic{ + Pos: names[0].Pos(), + End: typ.End(), + Message: fmt.Sprintf("var %s %s may be simplified using atomic.%s", v.Name(), oldType, newType), + SuggestedFixes: []analysis.SuggestedFix{{ + Message: fmt.Sprintf("Replace %s by atomic.%s", oldType, newType), + TextEdits: edits, + }}, + }) + } + + return nil, nil +} diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go index 0425faf8d4..7aa7046b39 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go @@ -65,6 +65,26 @@ This analyzer is currently disabled by default as the transformation does not preserve the nilness of the base slice in all cases; see https://go.dev/issue/73557. +# Analyzer atomictypes + +atomictypes: replace basic types in sync/atomic calls with atomic types + +The atomictypes analyzer suggests replacing the primitive sync/atomic functions with +the strongly typed atomic wrapper types introduced in Go1.19 (e.g. +atomic.Int32). For example, + + var x int32 + atomic.AddInt32(&x, 1) + +would become + + var x atomic.Int32 + x.Add(1) + +The atomic types are safer because they don't allow non-atomic access, which is +a common source of bugs. These types also resolve memory alignment issues that +plagued the old atomic functions on 32-bit architectures. + # Analyzer bloop bloop: replace for-range over b.N with b.Loop @@ -263,11 +283,15 @@ is known at compile time, for example: reflect.TypeOf(uint32(0)) -> reflect.TypeFor[uint32]() reflect.TypeOf((*ast.File)(nil)) -> reflect.TypeFor[*ast.File]() -It also offers a fix to simplify the construction below, which uses +It also offers a fix to simplify the constructions below, which use reflect.TypeOf to return the runtime type for an interface type, reflect.TypeOf((*io.Reader)(nil)).Elem() +or: + + reflect.TypeOf([]io.Reader(nil)).Elem() + to: reflect.TypeFor[io.Reader]() @@ -460,14 +484,15 @@ is replaced by: This avoids quadratic memory allocation and improves performance. -The analyzer requires that all references to s except the final one +The analyzer requires that all references to s before the final uses are += operations. To avoid warning about trivial cases, at least one must appear within a loop. The variable s must be a local variable, not a global or parameter. -The sole use of the finished string must be the last reference to the -variable s. (It may appear within an intervening loop or function literal, -since even s.String() is called repeatedly, it does not allocate memory.) +All uses of the finished string must come after the last += operation. +Each such use will be replaced by a call to strings.Builder's String method. +(These may appear within an intervening loop or function literal, since even +if s.String() is called repeatedly, it does not allocate memory.) Often the addend is a call to fmt.Sprintf, as in this example: @@ -519,11 +544,11 @@ where ptr is an unsafe.Pointer, is replaced by: unsafe.Add(ptr, n) -# Analyzer waitgroup +# Analyzer waitgroupgo -waitgroup: replace wg.Add(1)/go/wg.Done() with wg.Go +waitgroupgo: replace wg.Add(1)/go/wg.Done() with wg.Go -The waitgroup analyzer simplifies goroutine management with `sync.WaitGroup`. +The waitgroupgo analyzer simplifies goroutine management with `sync.WaitGroup`. It replaces the common pattern wg.Add(1) diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/errorsastype.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/errorsastype.go index d9a922f846..8603d54712 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/errorsastype.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/errorsastype.go @@ -125,22 +125,11 @@ func errorsastype(pass *analysis.Pass) (any, error) { errtype := types.TypeString(v.Type(), qual) // Choose a name for the "ok" variable. - // TODO(adonovan): this pattern also appears in stditerators, - // and is wanted elsewhere; factor. - okName := "ok" - if okVar := lookup(info, curCall, "ok"); okVar != nil { - // The name 'ok' is already declared, but - // don't choose a fresh name unless okVar - // is also used within the if-statement. - curIf := curCall.Parent() - for curUse := range index.Uses(okVar) { - if curIf.Contains(curUse) { - scope := info.Scopes[curIf.Node().(*ast.IfStmt)] - okName = refactor.FreshName(scope, v.Pos(), "ok") - break - } - } - } + // We generate a new name only if 'ok' is already declared at + // curCall and it also used within the if-statement. + curIf := curCall.Parent() + ifScope := info.Scopes[curIf.Node().(*ast.IfStmt)] + okName := freshName(info, index, ifScope, v.Pos(), curCall, curIf, token.NoPos, "ok") pass.Report(analysis.Diagnostic{ Pos: call.Fun.Pos(), @@ -189,7 +178,7 @@ func errorsastype(pass *analysis.Pass) (any, error) { // declaration of the typed error var. The var must not be // used outside the if statement. func canUseErrorsAsType(info *types.Info, index *typeindex.Index, curCall inspector.Cursor) (_ *types.Var, _ inspector.Cursor) { - if !astutil.IsChildOf(curCall, edge.IfStmt_Cond) { + if curCall.ParentEdgeKind() != edge.IfStmt_Cond { return // not beneath if statement } var ( @@ -221,7 +210,7 @@ func canUseErrorsAsType(info *types.Info, index *typeindex.Index, curCall inspec return // v used before/after if statement } } - if !astutil.IsChildOf(curDef, edge.ValueSpec_Names) { + if curDef.ParentEdgeKind() != edge.ValueSpec_Names { return // v not declared by "var v T" } var ( diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/fmtappendf.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/fmtappendf.go index 389f703466..8210654131 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/fmtappendf.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/fmtappendf.go @@ -7,6 +7,7 @@ package modernize import ( "fmt" "go/ast" + "go/constant" "go/types" "strings" @@ -16,6 +17,7 @@ import ( "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/fmtstr" "golang.org/x/tools/internal/typesinternal/typeindex" "golang.org/x/tools/internal/versions" ) @@ -45,10 +47,24 @@ func fmtappendf(pass *analysis.Pass) (any, error) { if ek, idx := curCall.ParentEdge(); ek == edge.CallExpr_Args && idx == 0 { // Is parent a T(fmt.SprintX(...)) conversion? conv := curCall.Parent().Node().(*ast.CallExpr) - tv := pass.TypesInfo.Types[conv.Fun] - if tv.IsType() && types.Identical(tv.Type, byteSliceType) && - analyzerutil.FileUsesGoVersion(pass, astutil.EnclosingFile(curCall), versions.Go1_19) { + info := pass.TypesInfo + tv := info.Types[conv.Fun] + if tv.IsType() && types.Identical(tv.Type, byteSliceType) { // Have: []byte(fmt.SprintX(...)) + if len(call.Args) == 0 { + continue + } + // fmt.Sprint(f) and fmt.Append(f) have different nil semantics + // when the format produces an empty string: + // []byte(fmt.Sprintf("")) returns an empty but non-nil + // []byte{}, while fmt.Appendf(nil, "") returns nil) so we + // should skip these cases. + if fn.Name() == "Sprint" || fn.Name() == "Sprintf" { + format := info.Types[call.Args[0]].Value + if format != nil && mayFormatEmpty(constant.StringVal(format)) { + continue + } + } // Find "Sprint" identifier. var id *ast.Ident @@ -62,13 +78,18 @@ func fmtappendf(pass *analysis.Pass) (any, error) { old, new := fn.Name(), strings.Replace(fn.Name(), "Sprint", "Append", 1) edits := []analysis.TextEdit{ { - // delete "[]byte(" + // Delete "[]byte(", including any spaces before the first argument. Pos: conv.Pos(), - End: conv.Lparen + 1, + End: conv.Args[0].Pos(), // always exactly one argument in a valid byte slice conversion }, { - // remove ")" - Pos: conv.Rparen, + // Delete ")", including any non-args (space or + // commas) that come before the right parenthesis. + // Leaving an extra comma here produces invalid + // code. (See golang/go#74709) + // Unfortunately, this and the edit above may result + // in deleting some comments. + Pos: conv.Args[0].End(), End: conv.Rparen + 1, }, { @@ -81,19 +102,8 @@ func fmtappendf(pass *analysis.Pass) (any, error) { NewText: []byte("nil, "), }, } - if len(conv.Args) == 1 { - arg := conv.Args[0] - // Determine if we have T(fmt.SprintX(...)<non-args, - // like a space or a comma>). If so, delete the non-args - // that come before the right parenthesis. Leaving an - // extra comma here produces invalid code. (See - // golang/go#74709) - if arg.End() < conv.Rparen { - edits = append(edits, analysis.TextEdit{ - Pos: arg.End(), - End: conv.Rparen, - }) - } + if !analyzerutil.FileUsesGoVersion(pass, astutil.EnclosingFile(curCall), versions.Go1_19) { + continue } pass.Report(analysis.Diagnostic{ Pos: conv.Pos(), @@ -110,3 +120,43 @@ func fmtappendf(pass *analysis.Pass) (any, error) { } return nil, nil } + +// mayFormatEmpty reports whether fmt.Sprintf might produce an empty string. +// It returns false in the following two cases: +// 1. formatStr contains non-operation characters. +// 2. formatStr contains formatting verbs besides s, v, x, X (verbs which may +// produce empty results) +// +// In all other cases it returns true. +func mayFormatEmpty(formatStr string) bool { + if formatStr == "" { + return true + } + operations, err := fmtstr.Parse(formatStr, 0) + if err != nil { + // If formatStr is malformed, the printf analyzer will report a + // diagnostic, so we can ignore this error. + // Calling Parse on a string without % formatters also returns an error, + // in which case we can safely return false. + return false + } + totalOpsLen := 0 + for _, op := range operations { + totalOpsLen += len(op.Text) + if !strings.ContainsRune("svxX", rune(op.Verb.Verb)) && op.Prec.Fixed != 0 { + // A non [s, v, x, X] formatter with non-zero precision cannot + // produce an empty string. + return false + } + } + // If the format string contains non-operation characters, it cannot produce + // the empty string. + if totalOpsLen != len(formatStr) { + return false + } + // If we get here, it means that all formatting verbs are %s, %v, %x, %X, + // and there are no additional non-operation characters. We conservatively + // report that this may format as an empty string, ignoring uses of + // precision and the values of the formatter args. + return true +} diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/minmax.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/minmax.go index 23a0977f21..b4b8dba3d1 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/minmax.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/minmax.go @@ -147,6 +147,13 @@ func minmax(pass *analysis.Pass) (any, error) { lhs0 := fassign.Lhs[0] rhs0 := fassign.Rhs[0] + // If the assignment occurs within a select + // comms clause (like "case lhs0 := <-rhs0:"), + // there's no way of rewriting it into a min/max call. + if prev.ParentEdgeKind() == edge.CommClause_Comm { + return + } + if astutil.EqualSyntax(lhs, lhs0) { if astutil.EqualSyntax(rhs, a) && (astutil.EqualSyntax(rhs0, b) || astutil.EqualSyntax(lhs0, b)) { sign = +sign @@ -209,7 +216,7 @@ func minmax(pass *analysis.Pass) (any, error) { // (This case would require introducing another block // if cond { ... } else { if a < b { lhs = rhs } } // and checking that there is no following "else".) - if astutil.IsChildOf(curIfStmt, edge.IfStmt_Else) { + if curIfStmt.ParentEdgeKind() == edge.IfStmt_Else { continue } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.go index f2947b1d88..42519eab53 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.go @@ -20,7 +20,9 @@ import ( "golang.org/x/tools/go/ast/edge" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/internal/analysis/analyzerutil" - "golang.org/x/tools/internal/astutil" + "golang.org/x/tools/internal/refactor" + "golang.org/x/tools/internal/typesinternal/typeindex" + "golang.org/x/tools/internal/moreiters" "golang.org/x/tools/internal/packagepath" "golang.org/x/tools/internal/stdlib" @@ -33,6 +35,7 @@ var doc string // Suite lists all modernize analyzers. var Suite = []*analysis.Analyzer{ AnyAnalyzer, + atomicTypesAnalyzer, // AppendClippedAnalyzer, // not nil-preserving! // BLoopAnalyzer, // may skew benchmark results, see golang/go#74967 FmtAppendfAnalyzer, @@ -54,7 +57,7 @@ var Suite = []*analysis.Analyzer{ StringsBuilderAnalyzer, TestingContextAnalyzer, unsafeFuncsAnalyzer, - WaitGroupAnalyzer, + WaitGroupGoAnalyzer, } // -- helpers -- @@ -114,7 +117,7 @@ func within(pass *analysis.Pass, pkgs ...string) bool { // unparenEnclosing removes enclosing parens from cur in // preparation for a call to [Cursor.ParentEdge]. func unparenEnclosing(cur inspector.Cursor) inspector.Cursor { - for astutil.IsChildOf(cur, edge.ParenExpr_X) { + for cur.ParentEdgeKind() == edge.ParenExpr_X { cur = cur.Parent() } return cur @@ -144,3 +147,37 @@ func lookup(info *types.Info, cur inspector.Cursor, name string) types.Object { } func first[T any](x T, _ any) T { return x } + +// freshName returns a fresh name at the given pos and scope based on preferredName. +// It generates a new name using refactor.FreshName only if: +// (a) the preferred name is already defined at definedCur, and +// (b) there are references to it from within usedCur. +// If useAfterPos.IsValid(), the references must be after +// useAfterPos within usedCur in order to warrant a fresh name. +// Otherwise, it returns preferredName, since shadowing is valid in this case. +// (declaredCur and usedCur may be identical in some use cases). +func freshName(info *types.Info, index *typeindex.Index, scope *types.Scope, pos token.Pos, defCur inspector.Cursor, useCur inspector.Cursor, useAfterPos token.Pos, preferredName string) string { + obj := lookup(info, defCur, preferredName) + if obj == nil { + // preferredName has not been declared here. + return preferredName + } + for use := range index.Uses(obj) { + if useCur.Contains(use) && use.Node().Pos() >= useAfterPos { + return refactor.FreshName(scope, pos, preferredName) + } + } + // Name is taken but not used in the given block; shadowing is acceptable. + return preferredName +} + +// isLocal reports whether obj is local to some function. +// Precondition: not a struct field or interface method. +func isLocal(obj types.Object) bool { + // [... 5=stmt 4=func 3=file 2=pkg 1=universe] + var depth int + for scope := obj.Parent(); scope != nil; scope = scope.Parent() { + depth++ + } + return depth >= 4 +} diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/plusbuild.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/plusbuild.go index 57b502ab80..ff6c90551e 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/plusbuild.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/plusbuild.go @@ -29,6 +29,8 @@ func init() { func plusbuild(pass *analysis.Pass) (any, error) { check := func(f *ast.File) { + // "//go:build" directives were added in go1.17, but + // we didn't start eliminating +build directives till go1.18. if !analyzerutil.FileUsesGoVersion(pass, f, versions.Go1_18) { return } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/rangeint.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/rangeint.go index 600c561fe3..03c7fd4f3f 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/rangeint.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/rangeint.go @@ -18,7 +18,6 @@ import ( "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/moreiters" "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal/typeindex" "golang.org/x/tools/internal/versions" @@ -142,15 +141,12 @@ func rangeint(pass *analysis.Pass) (any, error) { // Find references to i within the loop body. v := info.ObjectOf(index).(*types.Var) - // TODO(adonovan): use go1.25 v.Kind() == types.PackageVar - if typesinternal.IsPackageLevel(v) { + switch v.Kind() { + case types.PackageVar: continue nextLoop - } - - // If v is a named result, it is implicitly - // used after the loop (go.dev/issue/76880). - // TODO(adonovan): use go1.25 v.Kind() == types.ResultVar. - if moreiters.Contains(enclosingSignature(curLoop, info).Results().Variables(), v) { + case types.ResultVar: + // If v is a named result, it is implicitly + // used after the loop (go.dev/issue/76880). continue nextLoop } @@ -230,7 +226,7 @@ func rangeint(pass *analysis.Pass) (any, error) { // such as "const limit = 1e3", its effective type may // differ between the two forms. // In a for loop, it must be comparable with int i, - // for i := 0; i < limit; i++ + // for i := 0; i < limit; i++ {} // but in a range loop it would become a float, // for i := range limit {} // which is a type error. We need to convert it to int @@ -249,9 +245,24 @@ func rangeint(pass *analysis.Pass) (any, error) { beforeLimit, afterLimit = fmt.Sprintf("%s(", types.TypeString(tVar, qual)), ")" info2 := &types.Info{Types: make(map[ast.Expr]types.TypeAndValue)} if types.CheckExpr(pass.Fset, pass.Pkg, limit.Pos(), limit, info2) == nil { - tLimit := types.Default(info2.TypeOf(limit)) - if types.AssignableTo(tLimit, tVar) { - beforeLimit, afterLimit = "", "" + tLimit := info2.TypeOf(limit) + // Eliminate conversion when safe. + // + // Redundant conversions are not only unsightly but may in some cases cause + // architecture-specific types (e.g. syscall.Timespec.Nsec) to be inserted + // into otherwise portable files. + // + // The operand must have an integer type (not, say, '1e6') + // even when assigning to an existing integer variable. + if isInteger(tLimit) { + // When declaring a new var from an untyped limit, + // the limit's default type is what matters. + if init.Tok != token.ASSIGN { + tLimit = types.Default(tLimit) + } + if types.AssignableTo(tLimit, tVar) { + beforeLimit, afterLimit = "", "" + } } } } @@ -316,10 +327,10 @@ func isScalarLvalue(info *types.Info, curId inspector.Cursor) bool { cur := curId // Strip enclosing parens. - ek, _ := cur.ParentEdge() + ek := cur.ParentEdgeKind() for ek == edge.ParenExpr_X { cur = cur.Parent() - ek, _ = cur.ParentEdge() + ek = cur.ParentEdgeKind() } switch ek { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflect.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflect.go index 2611f6cf09..fb1f2e2efc 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflect.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflect.go @@ -47,6 +47,14 @@ func reflecttypefor(pass *analysis.Pass) (any, error) { // Have: reflect.TypeOf(expr) expr := call.Args[0] + + // reflect.TypeFor cannot be instantiated with an untyped nil. + // We use type information rather than checking the identifier name + // to correctly handle edge cases where "nil" is shadowed (e.g. nil := "nil"). + if info.Types[expr].IsNil() { + continue + } + if !typesinternal.NoEffects(info, expr) { continue // don't eliminate operand: may have effects } @@ -54,20 +62,20 @@ func reflecttypefor(pass *analysis.Pass) (any, error) { t := info.TypeOf(expr) var edits []analysis.TextEdit - // Special case for TypeOf((*T)(nil)).Elem(), - // needed when T is an interface type. - if astutil.IsChildOf(curCall, edge.SelectorExpr_X) { + // Special cases for TypeOf((*T)(nil)).Elem(), and + // TypeOf([]T(nil)).Elem(), needed when T is an interface type. + if curCall.ParentEdgeKind() == edge.SelectorExpr_X { curSel := unparenEnclosing(curCall).Parent() - if astutil.IsChildOf(curSel, edge.CallExpr_Fun) { - call2 := unparenEnclosing(curSel).Parent().Node().(*ast.CallExpr) + if curSel.ParentEdgeKind() == edge.CallExpr_Fun { + call2 := unparenEnclosing(curSel).Parent().Node().(*ast.CallExpr) // potentially .Elem() obj := typeutil.Callee(info, call2) if typesinternal.IsMethodNamed(obj, "reflect", "Type", "Elem") { - if ptr, ok := t.(*types.Pointer); ok { - // Have: TypeOf(expr).Elem() where expr : *T - t = ptr.Elem() - // reflect.TypeOf(expr).Elem() - // ------- - // reflect.TypeOf(expr) + // reflect.TypeOf(expr).Elem() + // ------- + // reflect.TypeOf(expr) + if typ, hasElem := t.(interface{ Elem() types.Type }); hasElem { + // Have: TypeOf(expr).Elem() where expr is *T, []T, [k]T, chan T, map[K]T, etc. + t = typ.Elem() edits = []analysis.TextEdit{{ Pos: call.End(), End: call2.End(), diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go index 3b32685266..c1d42188f6 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go @@ -231,7 +231,9 @@ func slicescontains(pass *analysis.Pass) (any, error) { // that might affected by melting down the loop. // // TODO(adonovan): relax check by analyzing branch target. + numBodyStmts := 0 for curBodyStmt := range curBody.Children() { + numBodyStmts += 1 if curBodyStmt != curLastStmt { for range curBodyStmt.Preorder((*ast.BranchStmt)(nil), (*ast.ReturnStmt)(nil)) { return @@ -292,7 +294,16 @@ func slicescontains(pass *analysis.Pass) (any, error) { case *ast.BranchStmt: if lastStmt.Tok == token.BREAK && lastStmt.Label == nil { // unlabeled break // Have: for ... { if ... { stmts; break } } - + if numBodyStmts == 1 { + // If the only stmt in the body is an unlabeled "break" that + // will get deleted in the fix, don't suggest a fix, as it + // produces confusing code: + // if slices.Contains(slice, f) {} + // Explicitly discarding the result isn't much better: + // _ = slices.Contains(slice, f) // just for effects + // See https://go.dev/issue/77677. + return + } var prevStmt ast.Stmt // previous statement to range (if any) if curPrev, ok := curRange.PrevSibling(); ok { // If the RangeStmt's previous sibling is a Stmt, @@ -316,12 +327,13 @@ func slicescontains(pass *analysis.Pass) (any, error) { len(assign.Rhs) == 1 { // Have: body={ lhs = rhs; break } + assignBool := isTrueOrFalse(info, assign.Rhs[0]) if prevAssign, ok := prevStmt.(*ast.AssignStmt); ok && len(prevAssign.Lhs) == 1 && len(prevAssign.Rhs) == 1 && + assignBool != 0 && // non-bool assignments don't apply in this case astutil.EqualSyntax(prevAssign.Lhs[0], assign.Lhs[0]) && - isTrueOrFalse(info, assign.Rhs[0]) == - -isTrueOrFalse(info, prevAssign.Rhs[0]) { + assignBool == -isTrueOrFalse(info, prevAssign.Rhs[0]) { // Have: // lhs = false @@ -332,7 +344,7 @@ func slicescontains(pass *analysis.Pass) (any, error) { // TODO(adonovan): // - support "var lhs bool = false" and variants. // - allow the break to be omitted. - neg := cond(isTrueOrFalse(info, assign.Rhs[0]) < 0, "!", "") + neg := cond(assignBool < 0, "!", "") report([]analysis.TextEdit{ // Replace "rhs" of previous assignment by [!]slices.Contains(...) { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stditerators.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stditerators.go index 95f2127fbf..86e1c8fd42 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stditerators.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stditerators.go @@ -18,7 +18,6 @@ import ( typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/goplsexport" - "golang.org/x/tools/internal/refactor" "golang.org/x/tools/internal/stdlib" "golang.org/x/tools/internal/typesinternal/typeindex" ) @@ -182,22 +181,9 @@ func stditerators(pass *analysis.Pass) (any, error) { } loop := curBody.Parent().Node() - - // Choose a fresh name only if - // (a) the preferred name is already declared here, and - // (b) there are references to it from the loop body. - // TODO(adonovan): this pattern also appears in errorsastype, - // and is wanted elsewhere; factor. - name := row.elemname - if v := lookup(info, curBody, name); v != nil { - // is it free in body? - for curUse := range index.Uses(v) { - if curBody.Contains(curUse) { - name = refactor.FreshName(info.Scopes[loop], loop.Pos(), name) - break - } - } - } + // We generate a new name only if the preferred name is already declared here + // and is used within the loop body. + name := freshName(info, index, info.Scopes[loop], loop.Pos(), curBody, curBody, token.NoPos, row.elemname) return name, nil } @@ -220,7 +206,7 @@ func stditerators(pass *analysis.Pass) (any, error) { ) // Analyze enclosing loop. - switch first(curLenCall.ParentEdge()) { + switch curLenCall.ParentEdgeKind() { case edge.BinaryExpr_Y: // pattern 1: for i := 0; i < x.Len(); i++ { ... } var ( @@ -228,7 +214,7 @@ func stditerators(pass *analysis.Pass) (any, error) { cmp = curCmp.Node().(*ast.BinaryExpr) ) if cmp.Op != token.LSS || - !astutil.IsChildOf(curCmp, edge.ForStmt_Cond) { + curCmp.ParentEdgeKind() != edge.ForStmt_Cond { continue } if id, ok := cmp.X.(*ast.Ident); ok { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsbuilder.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsbuilder.go index 3cb266986d..e89baa9b0e 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsbuilder.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsbuilder.go @@ -22,7 +22,6 @@ import ( typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/refactor" - "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal/typeindex" ) @@ -57,7 +56,7 @@ func stringsbuilder(pass *analysis.Pass) (any, error) { assign := curAssign.Node().(*ast.AssignStmt) if assign.Tok == token.ADD_ASSIGN && is[*ast.Ident](assign.Lhs[0]) { if v, ok := pass.TypesInfo.Uses[assign.Lhs[0].(*ast.Ident)].(*types.Var); ok && - !typesinternal.IsPackageLevel(v) && // TODO(adonovan): in go1.25, use v.Kind() == types.LocalVar && + v.Kind() == types.LocalVar && types.Identical(v.Type(), builtinString.Type()) { candidates[v] = true } @@ -102,7 +101,7 @@ nextcand: continue } - ek, _ := def.ParentEdge() + ek := def.ParentEdgeKind() if ek == edge.AssignStmt_Lhs && len(def.Parent().Node().(*ast.AssignStmt).Lhs) == 1 { // Have: s := expr @@ -254,8 +253,8 @@ nextcand: // var s string // for ... { s += expr } // - // - The final use of s must be as an rvalue (e.g. use(s), not &s). - // This will become s.String(). + // - All uses of s after the last += must be rvalue uses (e.g. use(s), not &s). + // Each of these will become s.String(). // // Perhaps surprisingly, it is fine for there to be an // intervening loop or lambda w.r.t. the declaration of s: @@ -270,19 +269,14 @@ nextcand: var ( numLoopAssigns int // number of += assignments within a loop loopAssign *ast.AssignStmt // first += assignment within a loop - seenRvalueUse bool // => we've seen the sole final use of s as an rvalue + seenRvalueUse bool // => we've seen at least one rvalue use of s ) for curUse := range index.Uses(v) { // Strip enclosing parens around Ident. - ek, _ := curUse.ParentEdge() + ek := curUse.ParentEdgeKind() for ek == edge.ParenExpr_X { curUse = curUse.Parent() - ek, _ = curUse.ParentEdge() - } - - // The rvalueUse must be the lexically last use. - if seenRvalueUse { - continue nextcand + ek = curUse.ParentEdgeKind() } // intervening reports whether cur has an ancestor of @@ -297,6 +291,11 @@ nextcand: } if ek == edge.AssignStmt_Lhs { + // After an rvalue use, no more assignments are allowed. + if seenRvalueUse { + continue nextcand + } + assign := curUse.Parent().Node().(*ast.AssignStmt) if assign.Tok != token.ADD_ASSIGN { continue nextcand @@ -317,9 +316,9 @@ nextcand: // ------------- - // s.WriteString(expr) edits = append(edits, []analysis.TextEdit{ - // replace += with .WriteString() + // replace " += " with ".WriteString(" { - Pos: assign.TokPos, + Pos: assign.Lhs[0].End(), End: assign.Rhs[0].Pos(), NewText: []byte(".WriteString("), }, diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go index 62088f0e91..6192c56fa3 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go @@ -22,7 +22,7 @@ import ( typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/goplsexport" - "golang.org/x/tools/internal/refactor" + "golang.org/x/tools/internal/moreiters" "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal/typeindex" "golang.org/x/tools/internal/versions" @@ -124,6 +124,8 @@ func stringscut(pass *analysis.Pass) (any, error) { bytesIndexByte = index.Object("bytes", "IndexByte") ) + scopeFixCount := make(map[*types.Scope]int) // the number of times we have offered a fix within a given scope in the current pass + for _, obj := range []types.Object{ stringsIndex, stringsIndexByte, @@ -183,7 +185,7 @@ 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. - negative, nonnegative, beforeSlice, afterSlice := checkIdxUses(pass.TypesInfo, index.Uses(iObj), s, substr) + negative, nonnegative, beforeSlice, afterSlice := checkIdxUses(pass.TypesInfo, index.Uses(iObj), s, substr, iObj) // 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. @@ -194,14 +196,48 @@ func stringscut(pass *analysis.Pass) (any, error) { // If the only uses are ok and !ok, don't suggest a Cut() fix - these should be using Contains() isContains := (len(negative) > 0 || len(nonnegative) > 0) && len(beforeSlice) == 0 && len(afterSlice) == 0 + enclosingBlock, ok := moreiters.First(curCall.Enclosing((*ast.BlockStmt)(nil))) + if !ok { + continue + } scope := iObj.Parent() - var ( - // TODO(adonovan): avoid FreshName when not needed; see errorsastype. - okVarName = refactor.FreshName(scope, iIdent.Pos(), "ok") - beforeVarName = refactor.FreshName(scope, iIdent.Pos(), "before") - afterVarName = refactor.FreshName(scope, iIdent.Pos(), "after") - foundVarName = refactor.FreshName(scope, iIdent.Pos(), "found") // for Contains() - ) + // Generate fresh names for ok, before, after, found, but only if + // they are defined by the end of the enclosing block and used + // within the enclosing block after the Index call. We need a Cursor + // for the end of the enclosing block, but we can't just find the + // Cursor at scope.End() because it corresponds to the entire + // enclosingBlock. Instead, get the last child of the enclosing + // block. + lastStmtCur, _ := enclosingBlock.LastChild() + lastStmt := lastStmtCur.Node() + + fresh := func(preferred string) string { + return freshName(info, index, scope, lastStmt.End(), lastStmtCur, enclosingBlock, iIdent.Pos(), preferred) + } + + var okVarName, beforeVarName, afterVarName, foundVarName string + if isContains { + foundVarName = fresh("found") + } else { + okVarName = fresh("ok") + beforeVarName = fresh("before") + afterVarName = fresh("after") + } + + // If we are already suggesting a fix within the index's scope, we + // must get fresh names for before, after and ok. + // This is a specific symptom of the general problem that analyzers + // can generate conflicting fixes. + if scopeFixCount[scope] > 0 { + suffix := scopeFixCount[scope] - 1 // start at 0 + if isContains { + foundVarName = fresh(fmt.Sprintf("%s%d", foundVarName, suffix)) + } else { + okVarName = fresh(fmt.Sprintf("%s%d", okVarName, suffix)) + beforeVarName = fresh(fmt.Sprintf("%s%d", beforeVarName, suffix)) + afterVarName = fresh(fmt.Sprintf("%s%d", afterVarName, suffix)) + } + } // If there will be no uses of ok, before, or after, use the // blank identifier instead. @@ -313,6 +349,7 @@ func stringscut(pass *analysis.Pass) (any, error) { }...) } } + scopeFixCount[scope]++ pass.Report(analysis.Diagnostic{ Pos: indexCall.Fun.Pos(), End: indexCall.Fun.End(), @@ -374,14 +411,31 @@ func indexArgValid(info *types.Info, index *typeindex.Index, expr ast.Expr, afte // 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) (negative, nonnegative, beforeSlice, afterSlice []ast.Expr) { +// +// Additionally, all beforeSlice and afterSlice uses must be dominated by a +// nonnegative guard on i (i.e., inside the body of an if whose condition +// checks i >= 0, or in the else of a negative check, or after an +// early-return negative check). This ensures that the rewrite from +// s[i+len(sep):] to "after" preserves semantics, since when i == -1, +// s[i+len(sep):] may yield a valid substring (e.g. s[0:] for single-byte +// separators), but "after" would be "". +// +// When len(substr)==1, it's safe to use s[i+1:] even when i < 0. +// Otherwise, each replacement of s[i+1:] must be guarded by a check +// that i is nonnegative. +func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr ast.Expr, iObj types.Object) (negative, nonnegative, beforeSlice, afterSlice []ast.Expr) { + requireGuard := true + if l := constSubstrLen(info, substr); l != -1 && l != 1 { + requireGuard = false + } + use := func(cur inspector.Cursor) bool { - ek, _ := cur.ParentEdge() + ek := cur.ParentEdgeKind() n := cur.Parent().Node() switch ek { case edge.BinaryExpr_X, edge.BinaryExpr_Y: check := n.(*ast.BinaryExpr) - switch checkIdxComparison(info, check) { + switch checkIdxComparison(info, check, iObj) { case -1: negative = append(negative, check) return true @@ -397,10 +451,10 @@ func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr a if slice, ok := cur.Parent().Parent().Node().(*ast.SliceExpr); ok && sameObject(info, s, slice.X) && slice.Max == nil { - if isBeforeSlice(info, ek, slice) { + if isBeforeSlice(info, ek, slice) && (!requireGuard || isSliceIndexGuarded(info, cur, iObj)) { beforeSlice = append(beforeSlice, slice) return true - } else if isAfterSlice(info, ek, slice, substr) { + } else if isAfterSlice(info, ek, slice, substr) && (!requireGuard || isSliceIndexGuarded(info, cur, iObj)) { afterSlice = append(afterSlice, slice) return true } @@ -410,10 +464,10 @@ func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr a // Check that the thing being sliced is s and that the slice doesn't // have a max index. if sameObject(info, s, slice.X) && slice.Max == nil { - if isBeforeSlice(info, ek, slice) { + if isBeforeSlice(info, ek, slice) && (!requireGuard || isSliceIndexGuarded(info, cur, iObj)) { beforeSlice = append(beforeSlice, slice) return true - } else if isAfterSlice(info, ek, slice, substr) { + } else if isAfterSlice(info, ek, slice, substr) && (!requireGuard || isSliceIndexGuarded(info, cur, iObj)) { afterSlice = append(afterSlice, slice) return true } @@ -435,7 +489,7 @@ func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr a // considered. func hasModifyingUses(info *types.Info, uses iter.Seq[inspector.Cursor], afterPos token.Pos) bool { for curUse := range uses { - ek, _ := curUse.ParentEdge() + ek := curUse.ParentEdgeKind() if ek == edge.AssignStmt_Lhs { if curUse.Node().Pos() <= afterPos { continue @@ -465,8 +519,15 @@ func hasModifyingUses(info *types.Info, uses iter.Seq[inspector.Cursor], afterPo // 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 { +func checkIdxComparison(info *types.Info, check *ast.BinaryExpr, iObj types.Object) int { + isI := func(e ast.Expr) bool { + id, ok := e.(*ast.Ident) + return ok && info.Uses[id] == iObj + } + if !isI(check.X) && !isI(check.Y) { + return 0 + } + // 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 { @@ -515,44 +576,49 @@ func isBeforeSlice(info *types.Info, ek edge.Kind, slice *ast.SliceExpr) bool { return ek == edge.SliceExpr_High && (slice.Low == nil || isZeroIntConst(info, slice.Low)) } -// isAfterSlice reports whether the SliceExpr is of the form s[i+len(substr):], -// or s[i + k:] where k is a const is equal to len(substr). -func isAfterSlice(info *types.Info, ek edge.Kind, slice *ast.SliceExpr, substr ast.Expr) bool { - lowExpr, ok := slice.Low.(*ast.BinaryExpr) - if !ok || slice.High != nil { - return false - } - // Returns true if the expression is a call to len(substr). - isLenCall := func(expr ast.Expr) bool { - call, ok := expr.(*ast.CallExpr) - if !ok || len(call.Args) != 1 { - return false - } - return sameObject(info, substr, call.Args[0]) && typeutil.Callee(info, call) == builtinLen - } - +// constSubstrLen returns the constant length of substr, or -1 if unknown. +func constSubstrLen(info *types.Info, substr ast.Expr) int { // Handle len([]byte(substr)) - if is[*ast.CallExpr](substr) { - call := substr.(*ast.CallExpr) + if call, ok := substr.(*ast.CallExpr); ok { tv := info.Types[call.Fun] if tv.IsType() && types.Identical(tv.Type, byteSliceType) { // Only one arg in []byte conversion. substr = call.Args[0] } } - substrLen := -1 substrVal := info.Types[substr].Value if substrVal != nil { switch substrVal.Kind() { case constant.String: - substrLen = len(constant.StringVal(substrVal)) + return len(constant.StringVal(substrVal)) case constant.Int: // constant.Value is a byte literal, e.g. bytes.IndexByte(_, 'a') // or a numeric byte literal, e.g. bytes.IndexByte(_, 65) - substrLen = 1 + // ([]byte(rune) is not legal.) + return 1 + } + } + return -1 +} + +// isAfterSlice reports whether the SliceExpr is of the form s[i+len(substr):], +// or s[i + k:] where k is a const is equal to len(substr). +func isAfterSlice(info *types.Info, ek edge.Kind, slice *ast.SliceExpr, substr ast.Expr) bool { + lowExpr, ok := slice.Low.(*ast.BinaryExpr) + if !ok || slice.High != nil { + return false + } + // Returns true if the expression is a call to len(substr). + isLenCall := func(expr ast.Expr) bool { + call, ok := expr.(*ast.CallExpr) + if !ok || len(call.Args) != 1 { + return false } + return sameObject(info, substr, call.Args[0]) && typeutil.Callee(info, call) == builtinLen } + substrLen := constSubstrLen(info, substr) + switch ek { case edge.BinaryExpr_X: kVal := info.Types[lowExpr.Y].Value @@ -578,6 +644,75 @@ func isAfterSlice(info *types.Info, ek edge.Kind, slice *ast.SliceExpr, substr a return false } +// isSliceIndexGuarded reports whether a use of the index variable i (at the given cursor) +// inside a slice expression is dominated by a nonnegative guard. +// A use is considered guarded if any of the following are true: +// - It is inside the Body of an IfStmt whose condition is a nonnegative check on i. +// - It is inside the Else of an IfStmt whose condition is a negative check on i. +// - It is preceded (in the same block) by an IfStmt whose condition is a +// negative check on i with a terminating body (e.g., early return). +// +// Conversely, a use is immediately rejected if: +// - It is inside the Body of an IfStmt whose condition is a negative check on i. +// - It is inside the Else of an IfStmt whose condition is a nonnegative check on i. +// +// We have already checked (see [hasModifyingUses]) that there are no +// intervening uses (incl. via aliases) of i that might alter its value. +func isSliceIndexGuarded(info *types.Info, cur inspector.Cursor, iObj types.Object) bool { + for anc := range cur.Enclosing() { + switch anc.ParentEdgeKind() { + case edge.IfStmt_Body, edge.IfStmt_Else: + ifStmt := anc.Parent().Node().(*ast.IfStmt) + check := condChecksIdx(info, ifStmt.Cond, iObj) + if anc.ParentEdgeKind() == edge.IfStmt_Else { + check = -check + } + if check > 0 { + return true // inside nonnegative-guarded block (i >= 0 here) + } + if check < 0 { + return false // inside negative-guarded block (i < 0 here) + } + case edge.BlockStmt_List: + // Check preceding siblings for early-return negative checks. + for sib, ok := anc.PrevSibling(); ok; sib, ok = sib.PrevSibling() { + ifStmt, ok := sib.Node().(*ast.IfStmt) + if ok && condChecksIdx(info, ifStmt.Cond, iObj) < 0 && bodyTerminates(ifStmt.Body) { + return true // preceded by early-return negative check + } + } + case edge.FuncDecl_Body, edge.FuncLit_Body: + return false // stop at function boundary + } + } + return false +} + +// condChecksIdx reports whether cond is a BinaryExpr that checks +// the index variable iObj for negativity or non-negativity. +// Returns -1 for negative (e.g. i < 0), +1 for nonnegative (e.g. i >= 0), 0 otherwise. +func condChecksIdx(info *types.Info, cond ast.Expr, iObj types.Object) int { + binExpr, ok := cond.(*ast.BinaryExpr) + if !ok { + return 0 + } + return checkIdxComparison(info, binExpr, iObj) +} + +// bodyTerminates reports whether the given block statement unconditionally +// terminates execution (via return, break, continue, or goto). +func bodyTerminates(block *ast.BlockStmt) bool { + if len(block.List) == 0 { + return false + } + last := block.List[len(block.List)-1] + switch last.(type) { + case *ast.ReturnStmt, *ast.BranchStmt: + return true // return, break, continue, goto + } + return false +} + // sameObject reports whether we know that the expressions resolve to the same object. func sameObject(info *types.Info, expr1, expr2 ast.Expr) bool { if ident1, ok := expr1.(*ast.Ident); ok { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscutprefix.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscutprefix.go index 7dc11308dd..ae63454005 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscutprefix.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscutprefix.go @@ -201,8 +201,7 @@ func stringscutprefix(pass *analysis.Pass) (any, error) { if astutil.EqualSyntax(lhs, bin.X) && astutil.EqualSyntax(call.Args[0], bin.Y) || (astutil.EqualSyntax(lhs, bin.Y) && astutil.EqualSyntax(call.Args[0], bin.X)) { - // TODO(adonovan): avoid FreshName when not needed; see errorsastype. - okVarName := refactor.FreshName(info.Scopes[ifStmt], ifStmt.Pos(), "ok") + okVarName := freshName(info, index, info.Scopes[ifStmt], ifStmt.Pos(), curIfStmt, curIfStmt, token.NoPos, "ok") // Have one of: // if rest := TrimPrefix(s, prefix); rest != s { (ditto Suffix) // if rest := TrimPrefix(s, prefix); s != rest { (ditto Suffix) diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/unsafefuncs.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/unsafefuncs.go index d3549e7103..191eba95c9 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/unsafefuncs.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/unsafefuncs.go @@ -56,11 +56,6 @@ func unsafefuncs(pass *analysis.Pass) (any, error) { tUnsafePointer = types.Typ[types.UnsafePointer] ) - isInteger := func(t types.Type) bool { - basic, ok := t.Underlying().(*types.Basic) - return ok && basic.Info()&types.IsInteger != 0 - } - // isConversion reports whether e is a conversion T(x). // If so, it returns T and x. isConversion := func(curExpr inspector.Cursor) (t types.Type, x inspector.Cursor) { @@ -88,7 +83,7 @@ func unsafefuncs(pass *analysis.Pass) (any, error) { if sum, ok := curSum.Node().(*ast.BinaryExpr); ok && sum.Op == token.ADD && types.Identical(info.TypeOf(sum.X), types.Typ[types.Uintptr]) && - astutil.IsChildOf(curSum, edge.CallExpr_Args) { + curSum.ParentEdgeKind() == edge.CallExpr_Args { // Have: sum ≡ T(x:...uintptr... + y:...uintptr...) curX := curSum.ChildAt(edge.BinaryExpr_X, -1) curY := curSum.ChildAt(edge.BinaryExpr_Y, -1) @@ -102,7 +97,7 @@ func unsafefuncs(pass *analysis.Pass) (any, error) { // Is sum.x converted from unsafe.Pointer? _, curPtr := isConversion(curX) - if !astutil.CursorValid(curPtr) { + if !curPtr.Valid() { continue } ptr := curPtr.Node().(ast.Expr) @@ -208,3 +203,8 @@ func deleteConv(cur inspector.Cursor) []analysis.TextEdit { }, } } + +func isInteger(t types.Type) bool { + basic, ok := t.Underlying().(*types.Basic) + return ok && basic.Info()&types.IsInteger != 0 +} diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/waitgroup.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/waitgroupgo.go index abf5885cee..9af2d3bdc7 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/waitgroup.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/waitgroupgo.go @@ -22,18 +22,18 @@ import ( "golang.org/x/tools/internal/versions" ) -var WaitGroupAnalyzer = &analysis.Analyzer{ - Name: "waitgroup", - Doc: analyzerutil.MustExtractDoc(doc, "waitgroup"), +var WaitGroupGoAnalyzer = &analysis.Analyzer{ + Name: "waitgroupgo", + Doc: analyzerutil.MustExtractDoc(doc, "waitgroupgo"), Requires: []*analysis.Analyzer{ inspect.Analyzer, typeindexanalyzer.Analyzer, }, Run: waitgroup, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#waitgroup", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#waitgroupgo", } -// The waitgroup pass replaces old more complex code with +// The waitgroupgo pass replaces old more complex code with // go1.25 added API WaitGroup.Go. // // Patterns: @@ -97,6 +97,9 @@ func waitgroup(pass *analysis.Pass) (any, error) { if !ok || len(goStmt.Call.Args) != 0 { continue // go argument is not func(){...}() } + if lit.Type.Results != nil && len(lit.Type.Results.List) > 0 { + continue // function literal has return values; wg.Go requires func() + } list := lit.Body.List if len(list) == 0 { continue diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go index 1afb07c452..1e130f4290 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go @@ -318,7 +318,7 @@ func findPrintLike(pass *analysis.Pass, res *Result) { // An interface method has no body, but acts // like an implicit call to each implementing method. - if w.curBody.Inspector() == nil { + if !w.curBody.Valid() { for impl := range impls[w.obj.(*types.Func)] { doCall(w, impl, nil) } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go index bc15ef8b96..cc67252426 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go @@ -122,7 +122,7 @@ Usage of %[1]s: os.Exit(0) } if len(args) != 1 || !strings.HasSuffix(args[0], ".cfg") { - log.Fatalf(`invoking "go tool vet" directly is unsupported; use "go vet"`) + log.Fatalf(`invoking "go tool %[1]s" directly is unsupported; use "go %[1]s"`, progname) } Run(args[0], analyzers) } diff --git a/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/cursor.go b/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/cursor.go index 60ad425f34..239b10c4da 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/cursor.go +++ b/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/cursor.go @@ -18,8 +18,11 @@ import ( // // 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. +// The zero value of Cursor is not valid. +// +// Call [Inspector.Root] to obtain a cursor for the virtual root node +// of the traversal. This is the sole valid cursor for which [Cursor.Node] +// returns nil. // // Use the following methods to navigate efficiently around the tree: // - for ancestors, use [Cursor.Parent] and [Cursor.Enclosing]; @@ -37,7 +40,7 @@ type Cursor struct { index int32 // index of push node; -1 for virtual root node } -// Root returns a cursor for the virtual root node, +// Root returns a valid cursor for the virtual root node, // whose children are the files provided to [New]. // // Its [Cursor.Node] method return nil. @@ -61,14 +64,23 @@ func (in *Inspector) At(index int32) Cursor { return Cursor{in, index} } +// Valid reports whether the cursor is valid. +// The zero value of cursor is invalid. +// Unless otherwise documented, it is not safe to call +// any other method on an invalid cursor. +func (c Cursor) Valid() bool { + return c.in != nil +} + // Inspector returns the cursor's Inspector. +// It returns nil if the Cursor is not valid. 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]. +// It is provided for use with [Inspector.At]. // // Index must not be called on the Root node. func (c Cursor) Index() int32 { @@ -89,7 +101,7 @@ func (c Cursor) Node() ast.Node { // String returns information about the cursor's node, if any. func (c Cursor) String() string { - if c.in == nil { + if !c.Valid() { return "(invalid)" } if c.index < 0 { @@ -233,6 +245,18 @@ func (c Cursor) ParentEdge() (edge.Kind, int) { return unpackEdgeKindAndIndex(events[pop].parent) } +// ParentEdgeKind returns the kind component of the result of [Cursor.ParentEdge]. +func (c Cursor) ParentEdgeKind() edge.Kind { + ek, _ := c.ParentEdge() + return ek +} + +// ParentEdgeIndex returns the index component of the result of [Cursor.ParentEdge]. +func (c Cursor) ParentEdgeIndex() int { + _, index := c.ParentEdge() + return index +} + // 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. diff --git a/src/cmd/vendor/golang.org/x/tools/internal/analysis/analyzerutil/version.go b/src/cmd/vendor/golang.org/x/tools/internal/analysis/analyzerutil/version.go index 0b9bcc37b6..700d53effc 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/analysis/analyzerutil/version.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/analysis/analyzerutil/version.go @@ -17,6 +17,9 @@ import ( // FileUsesGoVersion reports whether the specified file may use features of the // specified version of Go (e.g. "go1.24"). // +// It returns false when version information is not available, +// such as for parsed files that are ignored by the type checker. +// // Tip: we recommend using this check "late", just before calling // pass.Report, rather than "early" (when entering each ast.File, or // each candidate node of interest, during the traversal), because the @@ -24,7 +27,10 @@ import ( // fraction of files that pass most version checks is high and // increases over time. func FileUsesGoVersion(pass *analysis.Pass, file *ast.File, version string) (_res bool) { - fileVersion := pass.TypesInfo.FileVersions[file] + fileVersion, ok := pass.TypesInfo.FileVersions[file] + if !ok { + return false // be conservative in the absence of information (e.g. IgnoredFiles) + } // Standard packages that are part of toolchain bootstrapping // are not considered to use a version of Go later than the diff --git a/src/cmd/vendor/golang.org/x/tools/internal/analysis/driverutil/fix.go b/src/cmd/vendor/golang.org/x/tools/internal/analysis/driverutil/fix.go index 7769b39beb..4bda3f76bb 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/analysis/driverutil/fix.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/analysis/driverutil/fix.go @@ -386,11 +386,20 @@ func FormatSourceRemoveImports(pkg *types.Package, src []byte) ([]byte, error) { // particular the name that would implicitly be declared by a // non-renaming import of a given existing dependency. func removeUnneededImports(fset *token.FileSet, pkg *types.Package, file *ast.File) { - // Map each existing dependency to its default import name. + // Map each existing dependency and its transitive dependencies to its default import name. // (We'll need this to interpret non-renaming imports.) packageNames := make(map[string]string) + var visit func(pkg *types.Package) + visit = func(pkg *types.Package) { + if packageNames[pkg.Path()] == "" { + packageNames[pkg.Path()] = pkg.Name() + for _, imp := range pkg.Imports() { + visit(imp) + } + } + } for _, imp := range pkg.Imports() { - packageNames[imp.Path()] = imp.Name() + visit(imp) } // Compute the set of free names of the file, @@ -426,7 +435,7 @@ func removeUnneededImports(fset *token.FileSet, pkg *types.Package, file *ast.Fi } switch name { case "": - continue // assume it's a new import + continue // assume it's a new import, and we didn't find its default name while searching the import graph case ".": continue // dot imports are tricky case "_": diff --git a/src/cmd/vendor/golang.org/x/tools/internal/astutil/comment.go b/src/cmd/vendor/golang.org/x/tools/internal/astutil/comment.go index 7e52aeaaac..5ed4765c72 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/astutil/comment.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/astutil/comment.go @@ -31,7 +31,7 @@ func Deprecation(doc *ast.CommentGroup) string { // -- plundered from the future (CL 605517, issue #68021) -- -// TODO(adonovan): replace with ast.Directive after go1.25 (#68021). +// TODO(adonovan): replace with ast.Directive in go1.26 (#68021). // Beware of our local mods to handle analysistest // "want" comments on the same line. diff --git a/src/cmd/vendor/golang.org/x/tools/internal/astutil/stringlit.go b/src/cmd/vendor/golang.org/x/tools/internal/astutil/stringlit.go index ce1e7de882..eb49d45125 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/astutil/stringlit.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/astutil/stringlit.go @@ -40,20 +40,64 @@ func PosInStringLiteral(lit *ast.BasicLit, offset int) (token.Pos, error) { return 0, fmt.Errorf("invalid offset") } + pos, _ := walkStringLiteral(lit, lit.End(), offset) + return pos, nil +} + +// OffsetInStringLiteral returns the byte offset within the logical (unquoted) +// string corresponding to the specified source position. +func OffsetInStringLiteral(lit *ast.BasicLit, pos token.Pos) (int, error) { + if !NodeContainsPos(lit, pos) { + return 0, fmt.Errorf("invalid position") + } + + raw := lit.Value + + value, err := strconv.Unquote(raw) + if err != nil { + return 0, err + } + + _, offset := walkStringLiteral(lit, pos, len(value)) + return offset, nil +} + +// walkStringLiteral iterates through the raw string literal to map between +// a file position and a logical byte offset. It stops when it reaches +// either the targetPos or the targetOffset. +// +// TODO(hxjiang): consider making an iterator. +func walkStringLiteral(lit *ast.BasicLit, targetPos token.Pos, targetOffset int) (token.Pos, int) { + raw := lit.Value + norm := int(lit.End()-lit.Pos()) > len(lit.Value) + // remove quotes quote := raw[0] // '"' or '`' raw = raw[1 : len(raw)-1] var ( - i = 0 // byte index within logical value - pos = lit.ValuePos + 1 // position within literal + i = 0 // byte index within logical value + pos = lit.Pos() + 1 // position within literal ) - for raw != "" && i < offset { + + for raw != "" { r, _, rest, _ := strconv.UnquoteChar(raw, quote) // can't fail sz := len(raw) - len(rest) // length of literal char in raw bytes - pos += token.Pos(sz) + + nextPos := pos + token.Pos(sz) + if norm && r == '\n' { + nextPos++ + } + nextI := i + utf8.RuneLen(r) // length of logical char in "cooked" bytes + + if nextPos > targetPos || nextI > targetOffset { + break + } + raw = raw[sz:] - i += utf8.RuneLen(r) + i = nextI + pos = nextPos } - return pos, nil + + return pos, i } diff --git a/src/cmd/vendor/golang.org/x/tools/internal/astutil/util.go b/src/cmd/vendor/golang.org/x/tools/internal/astutil/util.go index 6820ba4cda..f211d2cc3d 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/astutil/util.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/astutil/util.go @@ -11,45 +11,10 @@ import ( "go/token" "strings" - "golang.org/x/tools/go/ast/edge" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/internal/moreiters" ) -// PreorderStack traverses the tree rooted at root, -// calling f before visiting each node. -// -// Each call to f provides the current node and traversal stack, -// consisting of the original value of stack appended with all nodes -// from root to n, excluding n itself. (This design allows calls -// to PreorderStack to be nested without double counting.) -// -// If f returns false, the traversal skips over that subtree. Unlike -// [ast.Inspect], no second call to f is made after visiting node n. -// In practice, the second call is nearly always used only to pop the -// stack, and it is surprisingly tricky to do this correctly; see -// https://go.dev/issue/73319. -// -// TODO(adonovan): replace with [ast.PreorderStack] when go1.25 is assured. -func PreorderStack(root ast.Node, stack []ast.Node, f func(n ast.Node, stack []ast.Node) bool) { - before := len(stack) - ast.Inspect(root, func(n ast.Node) bool { - if n != nil { - if !f(n, stack) { - // Do not push, as there will be no corresponding pop. - return false - } - stack = append(stack, n) // push - } else { - stack = stack[:len(stack)-1] // pop - } - return true - }) - if len(stack) != before { - panic("push/pop mismatch") - } -} - // NodeContains reports whether the Pos/End range of node n encloses // the given range. // @@ -73,14 +38,6 @@ func NodeContainsPos(n ast.Node, pos token.Pos) bool { return NodeRange(n).ContainsPos(pos) } -// IsChildOf reports whether cur.ParentEdge is ek. -// -// TODO(adonovan): promote to a method of Cursor. -func IsChildOf(cur inspector.Cursor, ek edge.Kind) bool { - got, _ := cur.ParentEdge() - return got == ek -} - // EnclosingFile returns the syntax tree for the file enclosing c. // // TODO(adonovan): promote this to a method of Cursor. @@ -204,19 +161,19 @@ func Select(curFile inspector.Cursor, start, end token.Pos) (_enclosing, _start, for cur := range curEnclosing.Preorder() { if rng.Contains(NodeRange(cur.Node())) { // The start node has the least Pos. - if !CursorValid(curStart) { + if !curStart.Valid() { curStart = cur } // The end node has the greatest End. // End positions do not change monotonically, // so we must compute the max. - if !CursorValid(curEnd) || + if !curEnd.Valid() || cur.Node().End() > curEnd.Node().End() { curEnd = cur } } } - if !CursorValid(curStart) { + if !curStart.Valid() { // The selection is valid (inside curEnclosing) but contains no // complete nodes. This happens for point selections (start == end), // or selections covering only only spaces, comments, and punctuation @@ -227,15 +184,69 @@ func Select(curFile inspector.Cursor, start, end token.Pos) (_enclosing, _start, return curEnclosing, curStart, curEnd, nil } -// CursorValid reports whether the cursor is valid. -// -// A valid cursor may yet be the virtual root node, -// cur.Inspector.Root(), which has no [Cursor.Node]. +var noCursor inspector.Cursor + +// MaybeParenthesize returns new, possibly wrapped in parens if needed +// to preserve operator precedence when it replaces old, whose parent +// is parentNode. // -// TODO(adonovan): move to cursorutil package, and move that package into x/tools. -// Ultimately, make this a method of Cursor. Needs a proposal. -func CursorValid(cur inspector.Cursor) bool { - return cur.Inspector() != nil +// (This would be more naturally written in terms of Cursor, but one of +// the callers--the inliner--does not have cursors handy.) +func MaybeParenthesize(parentNode ast.Node, old, new ast.Expr) ast.Expr { + if needsParens(parentNode, old, new) { + new = &ast.ParenExpr{X: new} + } + return new } -var noCursor inspector.Cursor +func needsParens(parentNode ast.Node, old, new ast.Expr) bool { + // An expression beneath a non-expression + // has no precedence ambiguity. + parent, ok := parentNode.(ast.Expr) + if !ok { + return false + } + + precedence := func(n ast.Node) int { + switch n := n.(type) { + case *ast.UnaryExpr, *ast.StarExpr: + return token.UnaryPrec + case *ast.BinaryExpr: + return n.Op.Precedence() + } + return -1 + } + + // Parens are not required if the new node + // is not unary or binary. + newprec := precedence(new) + if newprec < 0 { + return false + } + + // Parens are required if parent and child are both + // unary or binary and the parent has higher precedence. + if precedence(parent) > newprec { + return true + } + + // Was the old node the operand of a postfix operator? + // f().sel + // f()[i:j] + // f()[i] + // f().(T) + // f()(x) + switch parent := parent.(type) { + case *ast.SelectorExpr: + return parent.X == old + case *ast.IndexExpr: + return parent.X == old + case *ast.SliceExpr: + return parent.X == old + case *ast.TypeAssertExpr: + return parent.X == old + case *ast.CallExpr: + return parent.Fun == old + } + return false +} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/diff/unified.go b/src/cmd/vendor/golang.org/x/tools/internal/diff/unified.go index a6ebe9f950..df8f2fcc12 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/diff/unified.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/diff/unified.go @@ -20,7 +20,7 @@ const DefaultContextLines = 3 // The old and new labels are the names of the old and new files. // If the strings are equal, it returns the empty string. func Unified(oldLabel, newLabel, old, new string) string { - edits := Strings(old, new) + edits := Lines(old, new) unified, err := ToUnified(oldLabel, newLabel, old, edits, DefaultContextLines) if err != nil { // Can't happen: edits are consistent. diff --git a/src/cmd/vendor/golang.org/x/tools/internal/goplsexport/export.go b/src/cmd/vendor/golang.org/x/tools/internal/goplsexport/export.go index b0572f5968..e960b36db4 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/goplsexport/export.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/goplsexport/export.go @@ -14,4 +14,5 @@ var ( PlusBuildModernizer *analysis.Analyzer // = modernize.plusbuildAnalyzer StringsCutModernizer *analysis.Analyzer // = modernize.stringscutAnalyzer UnsafeFuncsModernizer *analysis.Analyzer // = modernize.unsafeFuncsAnalyzer + AtomicTypesModernizer *analysis.Analyzer // = modernize.atomicTypesAnalyzer ) diff --git a/src/cmd/vendor/golang.org/x/tools/internal/refactor/delete.go b/src/cmd/vendor/golang.org/x/tools/internal/refactor/delete.go index 54d0b5f038..25547f04f0 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/refactor/delete.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/refactor/delete.go @@ -32,7 +32,7 @@ import ( // If it cannot make the necessary edits, such as for a function // parameter or result, it returns nil. func DeleteVar(tokFile *token.File, info *types.Info, curId inspector.Cursor) []Edit { - switch ek, _ := curId.ParentEdge(); ek { + switch curId.ParentEdgeKind() { case edge.ValueSpec_Names: return deleteVarFromValueSpec(tokFile, info, curId) @@ -74,7 +74,7 @@ func deleteVarFromValueSpec(tokFile *token.File, info *types.Info, curIdent insp // at least one LHS, or for effects on RHS. // Blank out or delete just one LHS. - _, index := curIdent.ParentEdge() // index of LHS within ValueSpec.Names + index := curIdent.ParentEdgeIndex() // index of LHS within ValueSpec.Names // If there is no RHS, we can delete the LHS. if len(spec.Values) == 0 { @@ -181,7 +181,7 @@ func deleteVarFromAssignStmt(tokFile *token.File, info *types.Info, curIdent ins // If the assignment is 1:1 and the RHS has no effects, // we can delete the LHS and its corresponding RHS. - _, index := curIdent.ParentEdge() + index := curIdent.ParentEdgeIndex() if len(assign.Lhs) > 1 && len(assign.Lhs) == len(assign.Rhs) && typesinternal.NoEffects(info, assign.Rhs[index]) { @@ -259,7 +259,7 @@ func DeleteSpec(tokFile *token.File, curSpec inspector.Cursor) []Edit { } // Delete the spec and its comments. - _, index := curSpec.ParentEdge() // index of ValueSpec within GenDecl.Specs + index := curSpec.ParentEdgeIndex() // index of ValueSpec within GenDecl.Specs pos, end := spec.Pos(), spec.End() if doc := astutil.DocComment(spec); doc != nil { pos = doc.Pos() // leading comment @@ -288,7 +288,7 @@ func DeleteSpec(tokFile *token.File, curSpec inspector.Cursor) []Edit { func DeleteDecl(tokFile *token.File, curDecl inspector.Cursor) []Edit { decl := curDecl.Node().(ast.Decl) - ek, _ := curDecl.ParentEdge() + ek := curDecl.ParentEdgeKind() switch ek { case edge.DeclStmt_Decl: return DeleteStmt(tokFile, curDecl.Parent()) diff --git a/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/callee.go b/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/callee.go index 9a960bd293..815c8c0bba 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/callee.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/callee.go @@ -18,7 +18,6 @@ import ( "strings" "golang.org/x/tools/go/types/typeutil" - "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typesinternal" ) @@ -145,7 +144,7 @@ func AnalyzeCallee(logf func(string, ...any), fset *token.FileSet, pkg *types.Pa var f func(n ast.Node, stack []ast.Node) bool var stack []ast.Node stack = append(stack, decl.Type) // for scope of function itself - visit := func(n ast.Node, stack []ast.Node) { astutil.PreorderStack(n, stack, f) } + visit := func(n ast.Node, stack []ast.Node) { ast.PreorderStack(n, stack, f) } f = func(n ast.Node, stack []ast.Node) bool { switch n := n.(type) { case *ast.SelectorExpr: @@ -469,7 +468,7 @@ func analyzeParams(logf func(string, ...any), fset *token.FileSet, info *types.I fieldObjs := fieldObjs(sig) var stack []ast.Node stack = append(stack, decl.Type) // for scope of function itself - astutil.PreorderStack(decl.Body, stack, func(n ast.Node, stack []ast.Node) bool { + ast.PreorderStack(decl.Body, stack, func(n ast.Node, stack []ast.Node) bool { if id, ok := n.(*ast.Ident); ok { if v, ok := info.Uses[id].(*types.Var); ok { if pinfo, ok := paramInfos[v]; ok { @@ -533,7 +532,7 @@ func analyzeTypeParams(_ logger, fset *token.FileSet, info *types.Info, decl *as // TODO(jba): can we nevertheless combine this with the traversal in analyzeParams? var stack []ast.Node stack = append(stack, decl.Type) // for scope of function itself - astutil.PreorderStack(decl.Body, stack, func(n ast.Node, stack []ast.Node) bool { + ast.PreorderStack(decl.Body, stack, func(n ast.Node, stack []ast.Node) bool { if id, ok := n.(*ast.Ident); ok { if v, ok := info.Uses[id].(*types.TypeName); ok { if pinfo, ok := paramInfos[v]; ok { diff --git a/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/inline.go b/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/inline.go index f7e37fd7da..e968908fd6 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/inline.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/refactor/inline/inline.go @@ -132,8 +132,9 @@ func (st *state) inline() (*Result, error) { // the x subtree is subject to precedence ambiguity // (replacing x by p+q would give p+q[y:z] which is wrong) // but the y and z subtrees are safe. - if needsParens(caller.path, res.old, res.new) { - res.new = &ast.ParenExpr{X: res.new.(ast.Expr)} + if new, ok := res.new.(ast.Expr); ok { + parent := caller.path[slices.Index(caller.path, res.old)+1] + res.new = internalastutil.MaybeParenthesize(parent, res.old.(ast.Expr), new) } // Some reduction strategies return a new block holding the @@ -3137,73 +3138,6 @@ func last[T any](slice []T) T { return *new(T) } -// needsParens reports whether parens are required to avoid ambiguity -// around the new node replacing the specified old node (which is some -// ancestor of the CallExpr identified by its PathEnclosingInterval). -func needsParens(callPath []ast.Node, old, new ast.Node) bool { - // Find enclosing old node and its parent. - i := slices.Index(callPath, old) - if i == -1 { - panic("not found") - } - - // There is no precedence ambiguity when replacing - // (e.g.) a statement enclosing the call. - if !is[ast.Expr](old) { - return false - } - - // An expression beneath a non-expression - // has no precedence ambiguity. - parent, ok := callPath[i+1].(ast.Expr) - if !ok { - return false - } - - precedence := func(n ast.Node) int { - switch n := n.(type) { - case *ast.UnaryExpr, *ast.StarExpr: - return token.UnaryPrec - case *ast.BinaryExpr: - return n.Op.Precedence() - } - return -1 - } - - // Parens are not required if the new node - // is not unary or binary. - newprec := precedence(new) - if newprec < 0 { - return false - } - - // Parens are required if parent and child are both - // unary or binary and the parent has higher precedence. - if precedence(parent) > newprec { - return true - } - - // Was the old node the operand of a postfix operator? - // f().sel - // f()[i:j] - // f()[i] - // f().(T) - // f()(x) - switch parent := parent.(type) { - case *ast.SelectorExpr: - return parent.X == old - case *ast.IndexExpr: - return parent.X == old - case *ast.SliceExpr: - return parent.X == old - case *ast.TypeAssertExpr: - return parent.X == old - case *ast.CallExpr: - return parent.Fun == old - } - return false -} - // declares returns the set of lexical names declared by a // sequence of statements from the same block, excluding sub-blocks. // (Lexical names do not include control labels.) diff --git a/src/cmd/vendor/golang.org/x/tools/internal/refactor/refactor.go b/src/cmd/vendor/golang.org/x/tools/internal/refactor/refactor.go index 8664377f85..1d6c054332 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/refactor/refactor.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/refactor/refactor.go @@ -17,10 +17,9 @@ import ( // FreshName returns the name of an identifier that is undefined // at the specified position, based on the preferred name. // -// TODO(adonovan): refine this to choose a fresh name only when there -// would be a conflict with the existing declaration: it's fine to -// redeclare a name in a narrower scope so long as there are no free -// references to the outer name from within the narrower scope. +// export/use freshName in go/analysis/passes/modernize/modernize.go if you want +// to generate a fresh name only when necessary (i.e., there is both an existing +// declaration and some free reference to the name within a narrower scope) func FreshName(scope *types.Scope, pos token.Pos, preferred string) string { newName := preferred for i := 0; ; i++ { diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/typeindex/typeindex.go b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/typeindex/typeindex.go index 01ad7b9cf7..222ada55a2 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/typeindex/typeindex.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/typeindex/typeindex.go @@ -217,7 +217,7 @@ func (ix *Index) Selection(path, typename, name string) types.Object { func (ix *Index) Calls(callee types.Object) iter.Seq[inspector.Cursor] { return func(yield func(inspector.Cursor) bool) { for cur := range ix.Uses(callee) { - ek, _ := cur.ParentEdge() + ek := cur.ParentEdgeKind() // The call may be of the form f() or x.f(), // optionally with parens; ascend from f to call. @@ -231,19 +231,19 @@ func (ix *Index) Calls(callee types.Object) iter.Seq[inspector.Cursor] { // inverse unparen: f -> (f) for ek == edge.ParenExpr_X { cur = cur.Parent() - ek, _ = cur.ParentEdge() + ek = cur.ParentEdgeKind() } // ascend selector: f -> x.f if ek == edge.SelectorExpr_Sel { cur = cur.Parent() - ek, _ = cur.ParentEdge() + ek = cur.ParentEdgeKind() } // inverse unparen again for ek == edge.ParenExpr_X { cur = cur.Parent() - ek, _ = cur.ParentEdge() + ek = cur.ParentEdgeKind() } // ascend from f or x.f to call diff --git a/src/cmd/vendor/golang.org/x/tools/refactor/satisfy/find.go b/src/cmd/vendor/golang.org/x/tools/refactor/satisfy/find.go index bb38375531..3d21ce6d26 100644 --- a/src/cmd/vendor/golang.org/x/tools/refactor/satisfy/find.go +++ b/src/cmd/vendor/golang.org/x/tools/refactor/satisfy/find.go @@ -395,8 +395,11 @@ func (f *Finder) expr(e ast.Expr) types.Type { f.expr(e.X) case *ast.SelectorExpr: - if _, ok := f.info.Selections[e]; ok { - f.expr(e.X) // selection + if seln, ok := f.info.Selections[e]; ok { + // If e.X is a type (e.g., e is interface{ m() }.m), don't visit it. + if seln.Kind() != types.MethodExpr { + f.expr(e.X) + } } else { return f.info.Uses[e.Sel].Type() // qualified identifier } diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index ceed574b32..7b5b9aa37f 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -28,8 +28,8 @@ golang.org/x/arch/x86/x86asm # golang.org/x/build v0.0.0-20260122183339-3ba88df37c64 ## explicit; go 1.24.9 golang.org/x/build/relnote -# golang.org/x/mod v0.32.0 -## explicit; go 1.24.0 +# golang.org/x/mod v0.34.0 +## explicit; go 1.25.0 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/modfile golang.org/x/mod/module @@ -39,17 +39,17 @@ golang.org/x/mod/sumdb/dirhash golang.org/x/mod/sumdb/note golang.org/x/mod/sumdb/tlog golang.org/x/mod/zip -# golang.org/x/sync v0.19.0 -## explicit; go 1.24.0 +# golang.org/x/sync v0.20.0 +## explicit; go 1.25.0 golang.org/x/sync/errgroup golang.org/x/sync/semaphore -# golang.org/x/sys v0.40.1-0.20260116220947-d25a7aaff8c2 -## explicit; go 1.24.0 +# golang.org/x/sys v0.42.1-0.20260320201212-a76ec62d6c53 +## explicit; go 1.25.0 golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/telemetry v0.0.0-20260116145544-c6413dc483f5 -## explicit; go 1.24.0 +# golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c +## explicit; go 1.25.0 golang.org/x/telemetry golang.org/x/telemetry/counter golang.org/x/telemetry/counter/countertest @@ -73,8 +73,8 @@ 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.41.1-0.20260122210857-a60613f0795e -## explicit; go 1.24.0 +# golang.org/x/tools v0.43.1-0.20260319213245-5d7afbc08aec +## explicit; go 1.25.0 golang.org/x/tools/cmd/bisect golang.org/x/tools/cover golang.org/x/tools/go/analysis |
